Search code examples
typescriptgenericsmappingtypescript-generics

Create a TypeScript generic type that maps arbitrarily named types to other types


I've got some code that maps 'to -> from' types like this:

/**
 * From types:
 */
type Kitten = {
    kind: 'kitten';
};
type Puppy = {
    kind: 'puppy';
};

/**
 * To types:
 */
type Cat = {
    kind: 'cat';
};
type Dog = {
    kind: 'dog';
};

/**
 * Existing generic "condtional type" that converts any "FROM" -> "TO" using nested ternaries (slow when big):
 */
type ConvertType<T> = 
      T extends Kitten ? Cat 
    : T extends Puppy  ? Dog 
    : never;

However in reality, there's 100+ pair mappings, and therefore nested ternary conditions, which really slows TypeScript down.

Is there some better way to do this without using a slow "conditional type"?

I know it could also be done with a dummy function with overloads like this...

function convert_function(input: Kitten): Cat;
function convert_function(input: Puppy): Dog;
function convert_function(input: unknown) {
    return input;
}

...but I'm wondering if it can just be done purely with the typing system alone, as I am already with ConvertType<T>, but without the performance issues.


Solution

  • You should be able to make a union representing the pairings you care about, like

    type TypeMap = [Kitten, Cat] | [Puppy, Dog]
    

    That uses tuples to pair things, but you could use any type you want, like {i: Kitten, o: Cat} | {i: Puppy, o: Dog}, as long as it's consistent.

    And then a single distributive conditional type can filter it appropriately:

    type ConvertType<T> = Extract<TypeMap, [T, any]>[1]
    

    This uses the Extract utility type (implemented as a distributive conditional type) to filter TypeMap to just the entry whose first element is T, and then it indexes into that entry to get the second element (with index of 1).

    Let's test it out:

    type X = ConvertType<Kitten> 
    //   ^? type X = Cat
    type Y = ConvertType<Puppy>
    //   ^? type Y = Dog
    

    Looks good.

    Playground link to code