Pesquisa de site

typescript - Como obter o tipo de retorno para parâmetros específicos de uma função genérica?


Eu gostaria de criar um tipo utilitário que recebe uma assinatura de uma função genérica e seus argumentos. Ele deve resultar no tipo de retorno da assinatura como se fosse executado com os argumentos fornecidos.

Eu tentei isto:

type GetReturnType<T extends (...args: any) => any, B> = T extends (...args: any) => infer R ? R : never;

Mas todos os genéricos acabam sendo unknown ou never.

Algumas funções de exemplo que eu poderia querer passar:

function myFn1<T>(x: T) {
    return {
        a: x,
    }
}

function myFn2<T>(x: T) {
    return [x];
}

function myFn3<T>(x: T) {
    if (typeof x == "string") {
        return "yes" as (T extends string ? "yes" : "no");
    } else {
        return "no" as (T extends string ? "yes" : "no");
    }
}

E como eu gostaria que GetReturnType fosse usado:

type Return1 = GetReturnType<typeof myFn1, [x: number]>; // expected {a: number}
type Return2 = GetReturnType<typeof myFn2, [x: number]>; // expected number[]
type Return3A = GetReturnType<typeof myFn3, [x: number]>; // expected "no"
type Return3B = GetReturnType<typeof myFn3, [x: string]>; // expected "yes"

Link do playground

O TypeScript já possui um sistema para inferir os tipos genéricos de funções. Por exemplo, myFn3("some string") irá inferir o primeiro argumento genérico como sendo uma string e, por sua vez, o valor retornado será "yes". Eu gostaria de utilizar esse sistema ao tentar obter o tipo de retorno. Outras perguntas no SO perguntam como fazer isso quando os genéricos são conhecidos, mas eu gostaria de saber como fazer isso quando os genéricos são inferidos.

Resposta #1

const to = <A>(value: A) => ({ value } as const);

type ApplyTo<A> = ReturnType<typeof to<A>>;

const map = <A extends unknown[]>(value: readonly [...A]) => value.map(to) as {
  [K in keyof A]: ApplyTo<A[K]>;
};


type t2 = ReturnType<typeof map<["a", "b", "c"]>>;
// type t2 = [{
//   readonly value: "a";
// }, {
//   readonly value: "b";
// }, {
//   readonly value: "c";
// }]

Acho que isso é o mais próximo que se chega. Seria melhor se pudéssemos passar to como um parâmetro, mas pelo que sei, isso não é possível.

Resposta #2

A mesma pergunta, por que eu posso retornar valor extra

type RX = { id: number; name: string };
type GetProduct = (name: string) => RX;

const getProduct: GetProduct = (name) => ({ id: 123, name, city: "" });

como Cidade aqui