打字稿映射类型组成

时间:2018-12-05 22:13:23

标签: typescript

在提出问题Typescript custom mapped type之后(感谢Titian Cernicova-Dragomir!)
我有这种映射类型:

export type ToArray<T> = {
    [P in keyof T]: T[P] extends Vector<infer U> ? Array<U> : T[P]
}

现在,我想和Pick一起加入。

<Pick<ToArray<Tag>, 'id' | 'name' | 'children' | 'hierarchyName'>>{
    id: t.id,
    name: t.name,
    children: t.children.toArray()
}

但是,编译器不会强迫我添加hierarchyName,如果我使用,则会发生这种情况:

<Pick<Tag, 'id' | 'name' | 'children' | 'hierarchyName'>>

那怎么样?

1 个答案:

答案 0 :(得分:1)

我假设有些定义看起来像您的代码中的定义(您的实际代码可能有所不同,但是此代码足以说明问题):

interface Vector<E> {
    elements: E[];
    toArray(): E[];
}

interface Tag {
    id: string;
    name: string;
    children: Vector<Tag>;
    hierarchyName: string;
}

let t: Tag;

您说的是,在此初始化程序中缺少一个属性时,您将得到预期的错误:

const v1 = <Pick<Tag, 'id' | 'name' | 'children' | 'hierarchyName'>>{
    id: t.id,
    name: t.name,
    children: t.children.toArray()
};

让我们尝试将其简化为一个最小的示例:删除Pick,并删除所有属性的初始化:

const v2 = <Tag>{
};

没有错误。

为什么?因为<Tag>{}type assertion,所以它与{} as Tag相同-您迫使编译器相信该值实际上具有Tag类型。

编译器很少报告类型声明的错误,毕竟类型声明是一种告诉编译器“我更了解”的方法。就您而言,仅由于Tag是递归类型,它具有children属性(其类型为Tag[])而被报告,并且以某种方式使编译器认为值永远不兼容类型:

// error
const v2 = <Tag>{       
    children: t.children.toArray()
};

使用ToArray进行转换使类型成为非递归类型:转换后的类型中的children保留为Tag类型,这与封闭类型不同,这使得错误消失了。

捕获这些错误的正确方法是对变量声明使用类型注释,而不是类型断言:

const v3: Tag = {}; // error