1

What I want to do, is have a function which takes an array of variables which can be one of a few types.

For each given type, the function's second argument (callback) will take an array of the counterpart types

FooRequest will map to Foo, BarRequest, to Bar, etc.

TypeScript Playground Link

type FooRequest = {
    _tag: 'foo',
}

type BarRequest = {
    _tag: 'bar',
}

type DurRequest = {
    _tag: 'dur',
}

type AllRequests = FooRequest | BarRequest | DurRequest;

type Foo = { t: 'foo', age: number };
type Bar = { t: 'bar', name: string };
type Dur = { t: 'dur', date: Date };

// chained conditional type mapping each "request" type to it's counterpart "result" type
type ConditionalOutput<T extends AllRequests> = 
    T extends FooRequest ? Foo : 
    T extends BarRequest ? Bar : 
    T extends DurRequest ? Dur : never;

/**
 * How do I define the callback `result` parameter so it's an array matching
 * the counterpart types for each of the given input types?
 * 
 * For example
 * input: [{ _tag: 'foo' }, { _tag: 'bar' }]
 * result type: [Foo, Bar]
 */
function makeArrayRequests<T extends AllRequests>(input: T[], callback: (result: ConditionalOutput<T>[]) => void) {
    // not part of the quesiton, but logic here to call the callback
}


makeArrayRequests([{ _tag: 'foo' }, { _tag: 'bar' }], ([a, b]) => {
    // I want a to be auto infered to be Foo and b to be Bar
    // but right now both a and b are `Foo | Bar`
});

Sean256
  • 2,849
  • 4
  • 30
  • 39

1 Answers1

1

Thanks to @CertainPerformance for pointing me to the fancy mapped tuple features, I was able to get something working. But it comes with one little catch, I have to add as const to the input parameter.

TypeScript Playground Link

type FooRequest = {
    _tag: 'foo',
}

type BarRequest = {
    _tag: 'bar',
}

type DurRequest = {
    _tag: 'dur',
}

type AllRequests = FooRequest | BarRequest | DurRequest;

type Foo = { t: 'foo', age: number };
type Bar = { t: 'bar', name: string };
type Dur = { t: 'dur', date: Date };

// chained conditional type mapping each "request" type to it's counterpart
type ConditionalOutput<T extends any> = 
    T extends FooRequest ? Foo : 
    T extends BarRequest ? Bar : 
    T extends DurRequest ? Dur : never;


type TupleMapper<A extends readonly AllRequests[]> = {
    [Index in keyof A]: ConditionalOutput<A[Index]>;
};

function makeArrayRequests<T extends readonly AllRequests[]>(input: T, callback: (results: TupleMapper<T>) => void) {

}

// `as const` is required but it works
makeArrayRequests([{ _tag: 'foo' }, { _tag: 'bar' }] as const, (v) => {
    const[
        a, // Foo
        b, // Bar
    ] = v;
    console.log(a.age);
    console.log(b.name);
});
Sean256
  • 2,849
  • 4
  • 30
  • 39