我有一个人为的例子,该例子来自我正在做的React回调。
interface A {
a: string
b: string
}
interface B {
a: string
b: string
c: string
}
function foo(fn: (a: A) => void, a: A) {
fn(a)
}
function bar(arg: B): void {
console.log(arg.c)
}
foo(bar, {a: 'a', b: 'b'}) // This works, unfortunately
在这里,bar
是foo
的有效参数,即使bar
的第一个参数的字段比foo
应当包含的字段多。这意味着在此处提供bar
作为回调的人会认为他们的论点中将有c
string
,但不会。
这不像没有对参数进行类型检查。以下正确失败:
function baz(arg: string) { }
foo(baz, {a: 'a', b: 'b'}) // This fails correctly
这里到底发生了什么,有没有一种方法可以指定类型,使行为更接近理想状态?
答案 0 :(得分:3)
这似乎是泛型的好用例。您要确保作为参数传入的类型与function参数兼容。如果这些是相同的通用参数,则编译器应检查它们是否一致。
interface A {
a: string
b: string
}
interface B {
a: string
b: string
c: string
}
function foo<T extends A>(fn: (a: T) => void, a: T & {}) {
fn(a)
}
function bar(arg: B): void {
console.log(arg.c)
}
foo(bar, {a: 'a', b: 'b'}) //error
请注意,a
的{{1}}参数被键入为foo
,而不仅仅是T&{}
。这样做是为了减少该推理站点的优先级。由于可以从T
或T
推断出fn
,因此我们希望确保编译器偏爱a
,即使它可以为fn
选择一种类型,使两个参数兼容(在这种情况下,该类型为T
)。我不记得我在哪里记录了此行为,但是我确定编译器团队的成员说,在可预见的将来,可以依赖它:-)
答案 1 :(得分:0)
我发现这很有趣,我认为这可以解释它为什么起作用,不幸的是,不能解释如何不允许出现类似情况...:(
https://www.typescriptlang.org/docs/handbook/type-compatibility.html
答案 2 :(得分:0)
接口是合约,它需要一种结构来提供某些属性(或行为)。任何支持接口概念的语言都是如此。
看这个例子:
class C implements A, B {
a: string
b: string
c: string
}
类型C
同时符合A
和B
。因此,无论何时需要C
或A
,您都应该能够使用B
的实例。如果不能的话,那会很奇怪。
如果您申请的工作需要计算机科学学位,那么即使您拥有其他学位,您也有资格。如果他们除了拥有其他技能之外没有其他原因拒绝您,您会感到不安。