Matthew
Matthew

Reputation: 268

TypeScript inferring tuple return types

I am attempting to infer a function that returns a tuple type in TypeScript but the compiler is always inferring the function returns the type [unknown, unknown].

type DoubleReturn<F> = F extends (...args: infer _) => [infer R1, infer R2]
    ? [R1, R2]
    : never;

export function TransformMapKV<K, V, F extends (key: K, value: V) => DoubleReturn<F>>(
    values: ReadonlyMap<K, V>,
    callback: F
): Map<DoubleReturn<F>[0], DoubleReturn<F>[0]> {
    const newMap = new Map<DoubleReturn<F>[0], DoubleReturn<F>[0]>();

    for (const [k, v] of values) {
        const [nk, nv] = callback(k, v);
        // typeof nk is unknown
        // typeof nv is unknown
        newMap.set(nk, nv);
    }

    return newMap;
}

And some test code:

// okay, x is [string, number]
type x = DoubleReturn<() => [string, number]>;

const m = new Map<string, string>();
m.set("abc", "123");
m.set("xyz", "789");

// okay, callback is (key: string, value: string) => [string, number]
const trM = TransformMapKV(m, (key, value): [string, number] => {
    return [key + "!", Number.parseInt(value)];
});

Edit: simplified the code but the issue still remains.

playground link

Upvotes: 0

Views: 648

Answers (1)

sidecus
sidecus

Reputation: 752

I am not a Typescript type inference expert but my gut feeling is that there is some inference loop - in order to infer the type of callback, it needs to know type parameter F, which is not specified in your TransformMapKV instance and it needs to be inferred from the parameter, which again requires the exact type of TransformMapKV.

Below should do the trick for your case:

export function TransformMapKV<K, V, R1, R2>(
    values: ReadonlyMap<K, V>,
    callback: (key: K, value: V) => [R1, R2]
): Map<R1, R2> {
    const newMap = new Map<R1, R2>();

    for (const [k, v] of values) {
        newMap.set(...callback(k, v))
    }

    return newMap;
}

Upvotes: 1

Related Questions