Search code examples
typescripttypesmapped-types

Generate Tuple Type using a mapped Type


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.


Solution

  • 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