user11680003
user11680003

Reputation:

why do I have to add 'keyof' in front of 'any' type when use type mapping

I want to create new types, rather than transform a specific one, below is my code:

type CustomMapped<K extends any , T> = {                         //can't compile
    [P in K]: T
};

it doesn't compile, and the error is :

Type K is not assignable to type 'string | number | symbol'

so I have to add keyof in front of any as:

type CustomMapped<K extends keyof any , T> = ...                 //can compile

I'm confused, if I rewrite the code as:

type CustomMapped<K extends string | number | symbol, T> = {    //also compile
    [P in K]: T
};

it compiles, so it means Type K is assignable to type string | number | symbol

so why the original error says Type K is not assignable to type string | number | symbol, and why I added keyof then it is OK?

Upvotes: 1

Views: 116

Answers (1)

skovy
skovy

Reputation: 5650

The in operator works to create mapped types.

It expects a union of strings, number or symbols to effectively iterate through. As demonstrated in the documentation:

type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };

K will become option1 on the first "iteration" and option2 on the second. However, if Keys were an object, this wouldn't work.

type Keys = { option1: any; option2: any };
type Flags = { [K in Keys]: boolean }; 
//                   ^^^^
// Type 'Keys' is not assignable to type 'string | number | symbol'.

Because Keys is not a string, number or symbol to iterate over, but an object. Since we want to iterate over the keys, this can be solved with the keyof operator to return 'option1' | 'option2'.

With your example, you can update this with [P in keyof K] which will evalue to [P in 'option1' | 'option2'] and work as expected:

type CustomMapped<K extends any, T> = { [P in keyof K]: T };

Upvotes: 1

Related Questions