打字稿泛型,Type' X'不能分配类型' Y'

时间:2018-01-06 09:31:14

标签: typescript generics

我有以下代码:

interface DummyTableRow {
    id: number;
    name: string;
}
interface RowChange<Row extends object> {
    newRow: Row | null;
    oldRow: Row | null;
}
interface RowChangeBag {
    Dummy: RowChangeList<DummyTableRow>;
}
type RowChangeList<Row extends object> = Array<RowChange<Row>>;

function add<
    Row extends object,
    T extends keyof RowChangeBag,
    // error on next line: Type 'DummyTableRow' is not assignable to type 'Row'.
    L extends RowChangeList<Row> = RowChangeBag[T]
    >(
    bag: RowChangeBag,
    tableName: T,
    newRow: Row | null,
    oldRow: Row | null,
) {
    bag[tableName].push({ newRow, oldRow });
}

为什么抱怨?为什么DummyTableRow无法分配给Row

或者这可能是一个&#39; bug&#39;打字稿? (我在本例中使用的是2.6.2版本)

2 个答案:

答案 0 :(得分:2)

这不是TypeScript错误。

您看到的行为可以简化为以下内容:

interface DummyTableRow {
    id: number;
    name: string;
}

function doing<Row>() {
    let a: DummyTableRow
    let b: Row
    // error on next line: Type 'DummyTableRow' is not assignable to type 'Row'.
    b = a
}

这里的问题是,由于Row是泛型类型,编译器无法判断a是否可以分配给b,因为b可以是使用时定义的任何类型,例如

doing<{ a: string }>()

答案 1 :(得分:1)

您的代码混合了两种名称相似但类型不兼容的类型:

  • DummyTableRow是你对行的抽象→确定
  • 泛型类型约束Row extends objectobjectDummyTableRow不兼容:idname属性缺失。 →只需Row extends DummyTableRow

其他问题:

  • RowChangeBag也必须是通用的。
  • RowChangeBag旨在存储多个RowChangeList,参见T extends keyof RowChangeBagbag[tableName]RowChangeList。在下面的代码中,我将它拆分为2个接口,这样就可以更容易地在其中添加表格。
  • 未使用L方法中的add泛型类型约束。

我建议增强代码可读性:

  • 首选以T开头的通用类型名称:RowTRow
  • 然后,DummyTableRow名称可以简化:仅Row
  • 避免使用null;仅使用undefined
  • 然后,将属性定义为可选而不是可为空。
  • 使用T[]而不是Array<T>,因为它更短,更适合JavaScript。

修正提案(在TypeScript Playground上测试):

interface Row {
    id: number;
    name: string;
}

interface RowChange<TRow extends Row> {
    newRow?: TRow;
    oldRow?: TRow;
}

type RowChangeList<TRow extends Row> = RowChange<TRow>[];

interface TableGroup<T> {
    Table1: T;
    Table2: T;
}

interface RowChangeBag<TRow extends Row>
    extends TableGroup<RowChangeList<TRow>> { }

function add<
    TRow extends Row,
    TKey extends keyof RowChangeBag<TRow>
>(
    bag: RowChangeBag<TRow>,
    tableName: TKey,
    newRow: TRow,
    oldRow: TRow,
) {
    bag[tableName].push({ newRow, oldRow });
}