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.
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.