Home > other >  Pick multiple generic inheritance within typescript type
Pick multiple generic inheritance within typescript type

Time:08-01

I constructed a Typescript type named AdditinalAttributes which accepts a generic. The type will return the first condition that matches.

I would want that the type will return the sum of the matching conditions.

Example:

  • Current = AdditinalAttributes<'number'>TypeableAttributes
  • Target = AdditinalAttributes<'number'>TypeableAttributes & NumericAttributes

Relevant Code:

type TypeableInputs = 'number' | 'text'
type DropdownInputs = ''
type NumericInputs  = 'number'

type AdditinalAttributes<T> = (

    T extends TypeableInputs ? 
        TypeableAttributes :

    T extends DropdownInputs ? 
        DropdownAttributes : 

    T extends NumericInputs  ? 
        NumericAttributes  :

    { }
)

CodePudding user response:

Since you want the output to be the intersection of the relevant types, and because the input types are keylike, you can build a helper type whose properties are automatically what you want:

type InputToAttributeMap =
    Record<TypeableInputs, TypeableAttributes> &
    Record<DropdownInputs, DropdownAttributes> &
    Record<NumericInputs, NumericAttributes>

This uses the Record<K, V> utility type, which means "an object type with keys of type K and values of type V". By intersecting these together we can avoid conditional types. The InputToAttributeMap type is equivalent to:

type InputToAttributeMap = {
    number: TypeableAttributes & NumericAttributes;
    text: TypeableAttributes;
    "": DropdownAttributes;
}

Then your AdditionalAttributes type just needs to index into this mapping type:

type AdditionalAttributes<K extends keyof InputToAttributeMap> =
    InputToAttributeMap[K]

Let's test it:

type Target = AdditionalAttributes<"number">
//type Target = TypeableAttributes & NumericAttributes

Looks good!

Playground link to code

CodePudding user response:

In case what you actually want is an intersection of your attributes types (as suggested by @jcalz), besides his simple solution, we can also adapt the below one which was for a union: simply build the intersection directly, but instead of having potentially never members, they would be unknown (otherwise any non matching condition busts the output type as never):

type Extends2<T, I, A> = T extends I ? A : unknown

type AdditinalAttributes4<T> =
  Extends2<T, TypeableInputs, TypeableAttributes>
  & Extends2<T, DropdownInputs, DropdownAttributes>
  & Extends2<T, NumericInputs, NumericAttributes>

Note that with this solution, in case the generic does not match any condition, the type outputs unknown, i.e. it accepts any type. This is also what happens with the original type in the question (as {}).

Playground Link


Original answer for a union:

A simple solution could be to just build the desired union directly, with some members of the union being possibly never (in case they are not desired), instead of using chained conditional types (which indeed can return only a single result):

type AdditinalAttributes2<T> =
  | T extends TypeableInputs ? TypeableAttributes : never
  | T extends DropdownInputs ? DropdownAttributes : never
  | T extends NumericInputs ? NumericAttributes : never

We can even build a utility type to factorize the expression:

type Extends<T, I, A> = T extends I ? A : never

type AdditinalAttributes3<T> =
  | Extends<T, TypeableInputs, TypeableAttributes>
  // etc.

Playground Link

  • Related