Gergő Horváth
Gergő Horváth

Reputation: 3705

Optionally extend props of a type in typescript?

Consider the next example:

enum Keys {
  A = 'a',
  B = 'b',
  C = 'c',
}
//imagine a lot more possible keys

type ExtendKeysPropsByKey = {
  [Keys.A]: {
    a: string
  }
  [Keys.B]: {
    b: string
  }
}
//Keys.C doesn't require extended props

type KeysPropsByKey = {
  [key in keyof Keys]: {
    key: key
  } & ExtendKeysPropsByKey[key]
  //errors: Type 'key' cannot be used to index type 'ExtendKeysPropsByKey'.
  //also tried:
  //ExtendKeysPropsByKey[key] ? ExtendKeysPropsByKey[key] : {}
  //ExtendKeysPropsByKey[key] || {}
  //key in keyof ExtendKeysPropsByKey ? ExtendKeysPropsByKey[key] : {}
  //neither works
}

So imagine that there's 50 possible Keys, and we want to create a type which initially contains a key prop with the actual value of Keys, and would like to be able to optionally extend the props, so the expected type for the above example would be:

{
  [Keys.A]: {
    key: Keys.A
    a: string
  }
  [Keys.B]: {
    key: Keys.B
    b: string
  }
  [Keys.C]: {
    key: Keys.C
  }
}

Any possible scenarios?

Upvotes: 0

Views: 145

Answers (1)

Maciej Sikora
Maciej Sikora

Reputation: 20162

I hope I get what you ask for :). So I made a type constructor with two parameters - Keys - it defines what keys we will have, KeyProps - type represents what props given key should have.

enum Keys {
  A = 'a',
  B = 'b',
  C = 'c',
}

type ExtendKeysPropsByKey = {
  [Keys.A]: {
    a: string
  }
  [Keys.B]: {
    b: string,
    c: number;
  }
}

type MakeT<Keys extends string, KeyProps extends Partial<Record<Keys, any>>> = {
  [K in Keys]: {key: K} & (K extends keyof KeyProps ? KeyProps[K] : {})
}

type Result = MakeT<Keys, ExtendKeysPropsByKey>
/**
type Result = {
    a: {
        key: Keys.A;
    } & {
        a: string;
    };
    b: {
        key: Keys.B;
    } & {
        b: string;
        c: number;
    };
    c: {
        key: Keys.C;
    };
}
*/

Explanation:

  • KeyProps extends Partial<Record<Keys, any>> we define second argument as record with keys we want but also it is partial, as not all keys we require.
  • {key: K} & (K extends keyof KeyProps ? KeyProps[K] : {}) we say our value at given key will have property key equal our key, and we intersect it with props given in KeyProps object

Upvotes: 2

Related Questions