在TypesScript中传递回调-为什么回调的参数不能匹配和编译?

时间:2018-07-21 05:46:02

标签: typescript

我有一个人为的例子,该例子来自我正在做的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

在这里,barfoo的有效参数,即使bar的第一个参数的字段比foo应当包含的字段多。这意味着在此处提供bar作为回调的人会认为他们的论点中将有c string,但不会。

这不像没有对参数进行类型检查。以下正确失败:

function baz(arg: string) { }

foo(baz, {a: 'a', b: 'b'}) // This fails correctly

这里到底发生了什么,有没有一种方法可以指定类型,使行为更接近理想状态?

3 个答案:

答案 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&{}。这样做是为了减少该推理站点的优先级。由于可以从TT推断出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同时符合AB。因此,无论何时需要CA,您都应该能够使用B的实例。如果不能的话,那会很奇怪。

如果您申请的工作需要计算机科学学位,那么即使您拥有其他学位,您也有资格。如果他们除了拥有其他技能之外没有其他原因拒绝您,您会感到不安。