javascripttypescriptobject

What does TCreate[keyof TCreate] mean in TypeScript?


I already know that that keyof TCreate produces literal union of its keys in the type TCreate. But I’m confused about what TCreate[keyof TCreate] represents.

Why do we write it this way?

How does it work in TypeScript?

Why does it produce a union of all possible value types, instead of preserving the mapping between a specific key and its corresponding value type? For example, given this type:

type TCreate = {
  id: number;
  name: string;
  active: boolean;
};

keyof TCreate gives "id" | "name" | "active"

TCreate[keyof TCreate] gives number | string | boolean.

I think I expected it to somehow keep the relationship like

["id", number] | ["name", string] | ["active", boolean]

instead of collapsing everything into a union.

Could someone explain why it works this way??


Solution

  • I’m confused about what TCreate[keyof TCreate] represents.

    Why do we write it this way?

    How does it work in TypeScript?

    The type syntax A[B] is called an "indexed access type". It pulls out the property type(s) of A corresponding to the key(s) B. For example:

    type A = { key1: string; key2: number; key3: boolean }
    type Example = A['key1']
    //   ^? - type Example = string
    

    This works when the key is a union type as well. For example:

    type A = { key1: string; key2: number; key3: boolean }
    type Example = A['key1' | 'key2']
    //   ^? - type Example = string | number
    

    You already know that keyof A gets a union type representing all the keys of A. Combining that with what I've explained above, A[keyof A] will resolve to a union of all the property value types of A. For example:

    type A = { key1: string; key2: number; key3: boolean }
    type Example = A[keyof A]
    //   ^? - type Example = string | number | boolean
    

    In the above example, keyof A is 'key1' | 'key2' | 'key3', so A[keyof A] is the same as A['key1' | 'key2' | 'key3'].


    I think I expected it to somehow keep the relationship like

    ["id", number] | ["name", string] | ["active", boolean]
    

    A type like that could be derived by mapping over the properties of the initial type. For example:

    type A = { key1: string; key2: number; key3: boolean }
    type Example = { [K in keyof A]: [K, A[K]] }[keyof A]
    //   ^? - type Example = ["key1", string] | ["key2", number] | ["key3", boolean]