【问题标题】:Method argument and return types override in typescript打字稿中的方法参数和返回类型覆盖
【发布时间】:2016-12-09 07:53:32
【问题描述】:

我不想在抽象类中描述一个抽象方法,它可以采用numberstring 并返回数字或字符串; 我使用| 符号告诉方法它的参数和返回类型可能因字符串而异。
然后我创建了两个类bc,它们是从抽象类a 扩展而来的,并尝试在没有参数和返回类型变化的情况下覆盖方法test()
接下来,我要声明变量 x 哪个类型可能类似于 bc 类,并且我正在根据随机语句创建其中一个类的实例。
最后我尝试调用test() 方法,但是TS 编译器给了我下面描述的错误。

abstract class a {
    abstract test(x: string | number): string | number;
}

class b extends a {
    test(x: number): number {
        return x;
    }
}


class c extends a {
    test(x: string): string {
        return x;
    }
}

let x: b | c;


if (Math.random() > 0.5) {
    x = new b()
} else {
    x = new c()
};

x.test(1)

这是来自 TS 编译器的错误:

Cannot invoke an expression whose type lacks a call signature. Type '((x: number) => number) | ((x: string) => string)' has no compatible call signatures. (property) test: ((x: number) => number) | ((x: string) => string)

也许我使用了错误的方法或者我误解了 TS 文档,如果是这样 - 请您指出我的目标的更好方法。
对不起,糟糕的类名和没有任何“小提琴” - 我找不到任何突出 TS 编译器错误的 js 游乐场网站,所以我推荐官方 TS Playground

【问题讨论】:

    标签: javascript typescript


    【解决方案1】:

    当您在 if 块中创建类的实例时,打字稿编译器无法确定 x 将是哪种类型。没关系,但问题是您尝试使用数字调用测试函数,而这只有在类型为 b 时才有可能。由于编译器认为 x 可能属于 c 类型,因此您会收到错误消息。

    您需要向编译器保证,当您调用 test 时,您调用的函数将始终与提供的参数匹配。

    您可以:

    1. 更改调用签名,以便两者都接受任何类型,这样编译器调用哪个方法无关紧要:

      class b extends a {
          test(x: any) {
              return x;
          }
      }
      
      class c extends a {
          test(x : any) {
              return x;
          }  
      }
      
    2. 在 if 块中调用方法:

      if (Math.random() > 0.5) {
          x = new b();
          x.test(1);
      } else {
          x = new c();
          x.test('1');
      }
      
    3. Typeguard 你的方法调用:

      if (x instanceof b)
          x.test(1);
      else if(x instanceof c)
          x.test('1');
      

    查看关于联合类型和类型保护的手册:https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types

    编辑: 对您的建议是,您不必在每次调用时都输入类型,而是在方法本身中完成类型检查。这样做的缺点是可以使用不正确的参数调用方法,而不会收到编译器的警告。这是一个看起来如何的示例:

    abstract class a {
        protected abstract internal(x: any): any;
    
        public test(x: string | number): string | number {
            return this.internal(x);
        }
    }
    
    class b extends a {
        protected internal(x) {
            if (typeof x === "number") 
                return x;
            else
                throw new Error("Invalid argument");
        }
    }
    
    
    class c extends a {
        protected internal(x) {
            if (typeof x === "string")
                return x;
            else
                throw new Error("Invalid argument");
        }
    }
    
    let x: b | c;
    
    
    if (Math.random() > 0.5) {
        x = new b();
    } else {
        x = new c();
    }
    
    x.test(1);
    

    【讨论】:

    • 谢谢。你能告诉我是否每次都需要对我的方法调用进行类型保护,还是有一些“一次性使用”的解决方案?
    • 在不了解您要完成的工作的情况下准确回答有点困难,但我已经用一个您可以尝试的示例编辑了我的答案。
    【解决方案2】:

    您将 x 声明为 b 或 c,然后尝试通过使其仅 b 或仅 c 来重新定义它(据我了解,但我的理解有限) 根据您提供的编辑器,以下代码可以工作:

    abstract class a {
    abstract test(input: number | string )   
    }
    
    class b extends a {
        test(x: string) {
            return x;
        }
    }
    
    class c extends a {
        test(x : number) {
            return x;
        }  
    }
    
    let x//: b | c;
    
    if (Math.random() > 0.5) {
        x = new b()
    } else {
        x = new c()
    }
    
    x.test(1)
    

    【讨论】:

    • 是的,它有帮助,但由于我的 tsconfig 中的 "noImplicitAny": true 字符串,我收到关于隐含 any 类型的错误。我知道我可以删除它或使用any 类型声明变量x。我想要明确的变量声明 - 这可能吗?
    猜你喜欢
    • 2012-12-21
    • 2020-05-26
    • 1970-01-01
    • 1970-01-01
    • 2020-12-22
    • 2019-12-06
    • 2018-07-01
    • 2015-10-26
    • 2015-05-22
    相关资源
    最近更新 更多