What I want to do is the following:
Say I have a type:
type UpdateUnion =
{foo:number} |
{bar:string} |
{baz: {value1: number, value2: string}};
Is there a way to construct a mapped type like this?
type UpdateFunctions = {
foo: (value: number) => void,
bar: (value: string) => void,
baz: (value: {value1: number, value2:string}) => void
};
So far I've tried this:
type KeysOfUnion<T> = T extends T ? keyof T : never;
type UpdateFunctions = {
[Key in KeysOfUnion<UpdateUnion>]: (value: UpdateUnion[Key]) => void
}
This only seems to work if there is only one possible value in the union, otherwise I get the error: Type 'Key' cannot be used to index type 'UpdateUnion'
As an alternative approach, you can use key remapping to get the desired key:
type UpdateFunctions<T> = {
[K in T as keyof K]: (value: K[keyof K]) => void;
};
Key remapping allows us to change the key to something else. For instance, in the case, if we want to create a setter function for some property. If we have a property foo: number
we would need to create setFoo: (value: number) => void
.
Example without key remapping:
type Obj = {
foo: string;
bar: number;
oth: boolean;
};
type CreateSetters<T> = {
[K in keyof T]: (value: T[K]) => void;
};
// type Result = {
// foo: (value: string) => void;
// bar: (value: number) => void;
// oth: (value: boolean) => void;
// }
type Result = CreateSetters<Obj>;
With key remapping:
type CreateSetters<T> = {
[K in keyof T as `set${Capitalize<K & string>}`]: (value: T[K]) => void;
};
// type Result = {
// setFoo: (value: string) => void;
// setBar: (value: number) => void;
// setOth: (value: boolean) => void;
// }
type Result = CreateSetters<A>;
In the latter case, instead of K
we use set${Capitalize<K & string>
, which creates a key that starts with set
and capitalized K
which is achieved by using the built-in utility type Capitalize. Note that K
is string | number | symbol
and Capitalize
accepts only string
, thus we take the intersection of K
with string
, which guarantees that it will be a string.
Usage:
// type Result = {
// foo: (value: number) => void;
// bar: (value: string) => void;
// baz: (value: {
// value1: number;
// value2: string;
// }) => void;
// }
type Result = UpdateFunctions<UpdateUnion>