为什么 Typescript 映射元组类型在提供通用类型和直接类型时表现不同?

Aml*_*lau 5 tuples typescript

export type MyTuple = ["test", "othertest"];

type NotWorking = {
    [K in keyof MyTuple]: { value: MyTuple[K]};
};

type NotWorkingLengthType = NotWorking["length"]; // { value: 2 }

type Working<T>= {
    [P in keyof T]: { value: T[P] };
};

type MappedWorking = Working<MyTuple>;
type MappedWorkingLengthType = MappedWorking["length"]; // 2
Run Code Online (Sandbox Code Playgroud)

为什么在这种情况下它的行为有所不同?这让我很困惑。

jca*_*alz 3

这实际上看起来像一个错误,请参阅microsoft/TypeScript#27995


一般形式的映射类型{[K in keyof T]: ...T[K]...}被认为是同态T映射类型,并尽可能保留输入类型的结构。这种情况发生在可选和readonly键上(请参阅microsoft/TypeScript#12563 ),这也是在microsoft/TypeScript#26063中实现映射元组/数组时的意图。

为了使其发挥作用,这意味着编译器必须在评估后查看[K in keyof T]并记住保留. 当是泛型类型时会发生这种情况,对于可选/键,当是某种具体类型时也会发生这种情况:Tkeyof TTreadonlyT

type MyObj = { a?: string, readonly b: number };
type MyMappedObj = { [K in keyof MyObj]: { value: MyObj[K] } }
/* type MyMappedObj = {
    a?: {
        value: string | undefined;
    } | undefined;
    readonly b: {
        value: number;
    };
} */
Run Code Online (Sandbox Code Playgroud)

请注意,这仅当您的映射类型显式迭代具有精确in keyof”的键时才有效。如果您以其他方式计算键,或者将它们分配给类型别名,甚至只是将表达式括起来keyof,则该咒语将被破坏并且映射的类型不再是同态的:

type MyBadMappedObj = { [K in (keyof MyObj)]: { value: MyObj[K] } }
/* type MyMappedObj = {
    a: { // not optional
        value: string | undefined; 
    } 
    b: { // not readonly
        value: number;
    };
} */
Run Code Online (Sandbox Code Playgroud)

因此,映射像这样的键应该保留输出中{[K in keyof T]: ...}的结构。T


不幸的是,当引入映射数组/元组功能时,看起来这只是针对T泛型类型参数实现的,而不是针对特定的具体类型。在此评论中,实施者说:

这里的问题是,当我们实例化元组或数组的通用同态映射类型时,我们仅映射到元组和数组类型(请参见#26063)。我们或许也应该对具有keyof TwhereT是非泛型类型的同态映射类型执行此操作。

目前情况就是这样。也许这个问题最终会得到解决。在那之前,您可能应该使用像您的示例这样的中间泛型类型Working作为解决方法。


Playground 代码链接