interface Foo {
fn1(x: string): void;
}
class Test implements Foo {
public fn1 (): string {
return "111"
}
}
预期出现错误,例如:类测试错误地实现了接口Foo
因为函数的签名不正确
没有运气。演出没有任何错误
答案 0 :(得分:3)
这实际上不是错误的签名。可以使用单个参数作为字符串来调用它,并且只会忽略该参数。请记住,这是Javascript的工作方式,而Typescript会编译为Javascript:
function bar() {
console.log('bar');
}
bar('baz'); // no error at runtime in Javascript
同样,该接口的签名表示该方法无效,并且与实际返回字符串的方法兼容。如果调用者认为它无效,那么他们将永远不会观察到它返回一个字符串。如果没人认为obj.fn1()
没有返回任何内容,则不会写以下内容:
let result = obj.fn1(); // why am I assigning the return value of a void method?
因此,尽管参数和返回类型为“错误”,但实际上该方法可以安全地使用,就好像它接受了一个字符串却什么也不返回一样,因此它与接口的签名兼容。
这很好,但是Typescript 可以设计用于在此处报告错误,并且错误消息可能对您的示例很有帮助。但是在很多情况下,它没有帮助。考虑以下代码:
let names = ['Alice', 'Bob', 'Clive'];
let counts: Record<string, number> = {};
names.forEach(name => counts[name] = 0);
Array.prototype.forEach
的签名表明callbackfn
的类型应为:
(value: any, index: number, array: any[]) => void
但是箭头函数的类型为(name: string) => number
。这具有较少的参数和非空返回类型。因此,这与示例中的“错误”完全相同。当然,这是forEach
的基本和正确用法,对于Typescript抱怨它没有帮助。
答案 1 :(得分:1)
Typescript使用结构化类型确定类型兼容性。对于函数,这意味着您不需要为声明和实现使用完全相同的签名,只要编译器可以确定通过声明安全地调用该实现即可。
以这种方式考虑示例
interface Foo {
fn1(x: string): void;
}
class Test implements Foo {
constructor()
{}
public fn1 (): string {
return "111"
}
}
const fn1: Foo = new Test(); // error, fn1 has parameter, although Test fn1 doesn't expect one
const fn2: Test = new Test();
在fn1
而不是fn2
上出现错误的原因是,一旦分配完成,编译器将只知道变量的类型,而不是您最初分配给变量的类型,并将根据变量的实际类型。因此,对于fn1
来说,这意味着Foo.fn1
需要一个参数,而没有方法知道它不需要。对于fn2
,它将检查Test.fn1
,该参数不需要参数。
这是一种如何尝试保护您在打字稿中的类型的方法。