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!
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 {}
).
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.