Reputation: 73
I have a base type
type BaseProps = {
foo: boolean;
bar?: string;
};
This base type can be extended with 2 additional types:
type WithOkButton = {
useOkButton: boolean;
onOK(): void;
};
type WithCancelButton = {
useCancelButton: boolean;
onCancel(): void;
}
My goal is to have a type which has baseProps
and all the combinations of additional types:
WithOkButton
WithCancelButton
I can achieve my goal like this, but I don't like it. Can you please help me to find a better way?
type BaseWithOK = BaseProps & WithOkButton;
type BaseWithCancel = BaseProps & WithCancelButton;
type BaseWithBoth = BaseProps & WithOkButton & WithCancelButton;
type ResultType = BaseProps | BaseWithOK | BaseWithCancel | BaseWithBoth;
Here is how the code will look like if I need all the combinations for three buttons
type BaseProps = {
foo: boolean;
bar?: string;
};
type A = {
useA: boolean;
onClickA(): void;
};
type B = {
useB: boolean;
onClickB(): void;
};
type C = {
useC: boolean;
onClickC(): void;
};
type BasePropsWithA = A & BaseProps
type BasePropsWithB = B & BaseProps
type BasePropsWithC = C & BaseProps
type BasePropsWithAB = A & B & BaseProps
type BasePropsWithBC = B & C & BaseProps
type BasePropsWithAC = A & C & BaseProps
type BasePropsWithABC = A & B & C & BaseProps
type Props = BaseProps | BasePropsWithA | BasePropsWithB | BasePropsWithC
| BasePropsWithAB | BasePropsWithBC | BasePropsWithAC | BasePropsWithABC;
Upvotes: 2
Views: 158
Reputation: 73
I guess I found a solution. It can be done with help of an utility type like this:
type Optional<T> = T | {};
It allows to make all the possible combinations of additional types in one line of code:
type Props = BaseProps & Optional<WithOkButton> & Optional<WithCancelButton>;
So I suggest to solve the initial issue like this:
type Optional<T> = T | {};
type BaseProps = {
foo: boolean;
bar?: string;
};
type WithOkButton = {
useOkButton: boolean;
onOK(): void;
};
type WithCancelButton = {
useCancelButton: boolean;
onCancel(): void;
};
type Props = BaseProps & Optional<WithOkButton> & Optional<WithCancelButton>;
What do you think?
Upvotes: 1
Reputation: 26324
Essentially, you want combinations of the extensions all intersected with the base, so here I have a type that generates combinations from a tuple adapted from this JS impl:
type CombinationsImpl<Active extends ReadonlyArray<unknown>, Rest extends ReadonlyArray<unknown>, A extends ReadonlyArray<unknown>> =
[Active["length"] extends 0 ? true : false, Rest["length"] extends 0 ? true : false] extends [true, true]
? A
: Rest["length"] extends 0
? [...A, Active]
: Rest extends [infer Head, ...infer Tail]
? [...CombinationsImpl<[...Active, Head], Tail, A>, ...CombinationsImpl<Active, Tail, A>]
: never
type Combinations<A extends ReadonlyArray<unknown>> = CombinationsImpl<[], A, []>;
Then all we need to do is "loop" over the tuples with distributive conditional types and do the intersecting:
type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
type CreateProps<Base, Exts extends ReadonlyArray<unknown>> =
Combinations<Exts>[number] extends infer U extends ReadonlyArray<unknown>
? U extends U
? Base & UnionToIntersection<U[number]>
: never
: never
Keep in mind that for larger inputs, this may slow down your TSServer (thing that provides autocomplete/intellisense).
Upvotes: 0
Reputation: 111
According to official typescript docs here it is a recommended method.
Upvotes: 0