Home > database >  Is there any processing done inside the generic function parameter passing?
Is there any processing done inside the generic function parameter passing?

Time:12-08

Is there any difference between the parameter transfer type of the ts generic function and the internal definition type of the generic function?

I know that keyof can generate string or numeric literal union of its key for object type

type Data = { a: string, b: number } keyof Data;// 'a' | 'b'

But if I apply it to literal types, I will get union types similar to all its attributes
type Test = keyof 'a';
// result
type Test = number | typeof Symbol.iterator | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | ... 30 more ... | "padEnd"
I know that the "in" operator in typescript can be used to traverse the attribute names of the target type
type Test = {
    [P in keyof 'a']: '??'
};
type T = Test;
// result
type T = {
    [x: number]: "??";
    toString: "??";
    charAt: "??";
    charCodeAt: "??";
    concat: "??";
    indexOf: "??";
    lastIndexOf: "??";
    localeCompare: "??";
    match: "??";
    replace: "??";
    search: "??";
    slice: "??";
    split: "??";
    ... 30 more ...;
    [Symbol.iterator]: "??";
}

image

But I don't understand why the results are completely different once they are passed in through generics

type Test<V> = {
    [P in keyof V]: '??'
};
type T = Test<'a'>;
// result
type T = 'a'

image

CodePudding user response:

This has to do with whether or not the mapped type in question is perceived by the compiler as homomorphic (See What does "homomorphic mapped type" mean? for more detailed information about what "homomorphic" means in TypeScript). The answer is given in this comment on microsoft/TypeScript#41575 (slightly paraphrased):

The key issue here is that { [P in keyof T]: '??' }, where T is a naked type variable, is considered a homomorphic mapped type, whereas { [P in keyof 'a']: '??' } is not because keyof 'a' is eagerly resolved to a union of literal types. For homomorphic mapped types

  • if T is a union type we distribute the mapped type over the union,

  • if T is a primitive type no mapping is performed and the result is simply T,

  • if T is an array we map to an array where the element type has been transformed,

  • if T is a tuple we map to a tuple where the element types have been transformed, and

  • otherwise we map to an object type where the type of each property has been transformed.

We've had these distinctions in place for a very long time, not sure we can change it now without breaking lots of code.

So in { [P in keyof 'a']: '??' } the compiler eagerly computes keyof 'a' as a union of literal types such as "toString" | "charAt" (as well as number for the index signature and the relevant symbol key), and then dutifully maps over this union.

But in { [P in keyof T]: '??' } where T is generic, the compiler treats the whole thing as a homomorphic mapped type, and homomorphic mapped types on primitives like "a" just evaluate to their input directly, which is "a". At no point does the compiler even try to compute keyof "a" here.

  • Related