Good morning,
I'm trying to achieve the following,
Create a type that will take the keys from the following object
const obj = {
setName: () => void;
setAge: () => void;
}
And generate a Tuple type using a mapped type that makes
[ “setName”, “setAge” ]
I might be completely wrong here but for the mapped type I think this would work
type MyType<T> = {
[P in keyof T]: T[P]
}
But if T is a type from the object I showcased above, I would end up with the following type
type MappedType = {
setName: () => void;
setAge: () => void;
}
But what I want is
type MappedType = [setName, setAge]
Any help is greatly appreciated.
CodePudding user response:
what you need is UnionToTuple<U>
:
// T ====> [...T, E]
type Push<T, E> = T extends any[] ? [...T, E] : [];
// union to intersection of functions
// UnionToIoFn<'a' | 'b'>
// ====> ((x: 'a') => void) & ((x: 'b') => void)
// ====> that equal to { (x: 'a'): void; (x: 'b'): void }
type UnionToIoFn<U> =
(U extends any ? (k: (x: U) => void) => void : never) extends
((k: infer I) => void) ? I : never
// UnionPop<((x: 'a') => void) & ((x: 'b') => void)> ===> 'b'
// UnionPop<{ (x: 'a'): void; (x: 'b'): void }> ===> 'b'
type UnionPop<F> =
F extends ({ (x: infer X): void; })
? X
: never;
type UnionToTupleIter<U, Res> =
[U] extends [never]
? Res
: UnionToTupleIter<
Exclude<U, UnionPop<UnionToIoFn<U>>> ,
Push<Res, UnionPop<UnionToIoFn<U>>>
>
type UnionToTuple<U> = UnionToTupleIter<U, []>;
// test
const obj = {
setName: () => {},
setAge: () => {},
}
// You wanted
// type YouWanted = ["setAge", "setName"]
type YouWanted = UnionToTuple<keyof typeof obj>;
click here to try the code in ts playground
CodePudding user response:
Do you really need a Tuple type or is an Array type ok? If you just need ('setName', 'setAge')[]
, you can easily write (keyof typeof obj)[]
. I don't think it's necessary to make it into an tuple type (it's definitely possible tho), since I wouldn't rely on the order of object properties, and tuples typically infer a fixed ordering of things in it.