typescript - Extrahera returtyper på alla funktioner i ett gränssnitt


Föreställ dig att jag har flera gränssnitt:

interface ClassA {
    createMessage(basicInfo: string): string;
    calculateStuff(a: number, b: number): number;
    constant: number;
    someAction(): void;
}
interface ClassB {
    createTwoMessages(basicInfo: string): {message1: string, message2: string};
    calculateDifferentThing(a: number, b: number): number
}

och så vidare.

Hur skapar jag en typ som extraherar returtyperna för alla funktioner från gränssnitten? Det vore idealiskt om den filtrerade bort saker som inte är en funktion eller funktioner som returnerar void. Jag behöver att det fungerar på detta sätt:

type ExtractReturnTypes<T> = ???

type ReturnTypesOfClassA = ExtractReturnTypes<ClassA>
// = {createMessage: string, calculateStuff: number}
type ReturnTypesOfClassB = ExtractReturnTypes<ClassB>
// = {createTwoMessages: {message1: string, message2: string}, calculateDifferentThing: number}

I grunden är det en utvidgning av generiska typen ReturnType, men tillämpad på ett helt objekt istället för en enskild funktion, samtidigt som returtyperna namnges med de ursprungliga funktionsnamnen och saker som inte returnerar något filtreras bort. Men hittills har jag inte lyckats skapa det. Har någon en idé om hur jag kan göra detta, tack?

Svar #1

Du vill att ExtractReturnTypes<T> ska vara en mappad typ över egenskaperna i T, där vissa egenskapsnycklar undertrycks om de inte motsvarar funktioner med icke-void returvärden. Du kan använda nyckel-omkartläggning med as för att mappa de oönskade egenskapsnycklarna till never:

type ExtractReturnTypes<T> = { [K in keyof T as (
    T[K] extends (...args: any) => infer R ? (
        [R, void] extends [void, R] ? never : K
    ) : never
)]: T[K] extends (...args: any) => infer R ? R : never
}

Observera att i både den ommappade nyckeln och värdetypen måste vi beräkna den villkorliga typen T[K] extends (...args: any) => infer R ? ⋯ : ⋯ för att upptäcka och extrahera returtypen för egenskapsfunktionen. Detta är överflödigt eftersom man inte kan "korsa" gränsen mellan de ommappade nycklarna och de mappade värdena i en mappad typ. Hur som helst är den villkorliga typen lik den hur hjälpverktygstypen ReturnType<T> är implementerad.

För nyckeln undertrycker vi egenskapsnyckeln om värdet antingen inte är en funktion (det sista never) eller om det är en funktion som returnerar void. Att upptäcka en void-retur är svårt eftersom undefined är tilldelningsbart till void (se Varför är undefined tilldelningsbart till void?) och förmodligen vill vi inte undertrycka undefined (eller åtminstone är det inte frågan). Kontrollera [R, void] extends [void, R] kontrollerar för ömsesidig tilldelningsbarhet mellan R och void. Så om R är exakt void kommer det att fångas. Annars kommer det förmodligen inte att fångas (t.ex. om R är smalare än void, bredare än void eller orelaterat till void).


Låt oss prova det:

interface ClassA {
    createMessage(basicInfo: string): string;
    calculateStuff(a: number, b: number): number;
    constant: number;
    someAction(): void;

}   
type ReturnTypesOfClassA = ExtractReturnTypes<ClassA>
/* type ReturnTypesOfClassA = {
    createMessage: string;
    calculateStuff: number;
} */

interface ClassB {
    createTwoMessages(basicInfo: string): { message1: string, message2: string };
    calculateDifferentThing(a: number, b: number): number
}
type ReturnTypesOfClassB = ExtractReturnTypes<ClassB>
/* type ReturnTypesOfClassB = {
    createTwoMessages: {
        message1: string;
        message2: string;
    };
    calculateDifferentThing: number;
} */

Ser bra ut. Utgångstyperna matchar det du ville ha.

Lekplatslänk till kod

Relaterade artiklar