首页 > 解决方案 > Typescript 中与通用容器混淆的错误

问题描述

考虑这段代码:

class Base {}

class Foo<T extends Base> {
  constructor(public callback: (data: T) => void) {
  }
}

let map: Map<number, Foo<Base>> = new Map();

function rpcCall<T extends Base>(
  callback: (data: T) => void,
): void {
  map.set(0, new Foo<T>(callback));
}

它给了我这个错误:

typeFoo<T>的参数不能分配给 type 的参数Foo<Base>

类型Base不可分配给 type T

Base可分配给 type 的约束T,但T可以用不同的约束子类型实例化Base

我不明白为什么这不应该工作。错误消息似乎是正确的,但我不明白为什么这是一个错误。我希望 T被允许成为约束的不同子类型Base

此外,这确实有效:

class Base { }

class Foo<T extends Base> {
  constructor(public callback: (data: T) => void) {
  }
}

class Foo2<T extends Base> {
}

let map: Map<number, Foo<Base> | Foo2<Base>> = new Map();

function rpcCall<T extends Base>(
  callback: (data: T) => void,

): void {
  map.set(0, new Foo<T>(callback));
}

标签: typescript

解决方案


我将在您的代码中添加一些属性来说明这一点。

interface Base {
    bar: string;
}

interface Child extends Base {
    magic: number;
}

class Foo<T extends Base> {
    constructor(public callback: (data: T) => void) {
        // '{ bar: string; }' is assignable to the constraint of type 'T',
        // but 'T' could be instantiated with a different subtype of constraint 'Base'.
        callback({ bar: "sweet" });
    }
}

let map: Map<number, Foo<Base>> = new Map();

rpcCall((d: Child) => d.magic);

function rpcCall<T extends Base>(callback: (data: T) => void) {
    // 'Base' is assignable to the constraint of type 'T',
    // but 'T' could be instantiated with a different subtype of constraint 'Base'
    map.set(0, new Foo(callback));
}

哦亲爱的。更多错误!

如您所见,在里面Foo我试图调用您定义的回调,并传递给它一个 extends 对象Base,但它向我抛出了一个错误。

如果回调期待 aChild怎么办?当然,只要关心,任何扩展Base都很好Foo,但是你怎么知道回调本身期望什么?

如果你看一下我在rpcCall哪里合法地给它一个回调期望 a 的用法Child,我正在尝试使用该属性(在我的界面magic中标记为必需)。Child extends Base

基本上在某些时候可能会尝试使用存在的东西Base

如果您将泛型替换为简单地Base会使一些错误消失,但rpcCall((d: Child) => d.magic)不允许执行类似的操作。如果您不需要这些区域的非基础属性,这对您来说可能没问题。


您提供的第二个版本之所以有效,是因为Foo2它是一个空类(实际上,泛型完全被忽略了,因为您不使用它)。

一个空类相当于{},它基本上接受除nulland之外的所有内容undefined(据我所知)。当涉及联合类型时,任何“宽松”的参数都将优先于更严格的参数。

以下都是等价的(在这种情况下):

Map<number, Foo<Base> | Foo2<Base>>
Map<number, Foo<Base> | {}>
Map<number, Foo<Foo2<Base>>
Map<number, Foo<{}>

实际上,如果您通过管道传输| any到任何联合的末尾,则该联合实际上变为any.


推荐阅读