Typescript: Hämta typen av den sista parametern för en funktionstyp


Anta att jag har en funktionstyp, t.ex.

type somefntype = (a: number, b: string, c: boolean) => void

Jag behöver typen av den funktionens sista parameter:

type lastparamtype = LastParameter<somefntype> // lastparamtype == boolean

Hur definierar jag LastParameter?

Svar #1

Uppdatering :

För att korrekt härleda den sista parametertypen för valfria parametrar måste detta utökas något.

type Last<T extends any[]> = T extends [...infer I, infer L] ? L : T extends [...infer I, (infer L)?] ? L | undefined : never

type LastParameter<F extends (...args: any) => any> = Last<Parameters<F>>;
type somefntype = (a: number, b: string, c?: boolean) => void;
type lastparamtype = LastParameter<somefntype>; // boolean | undefined

Playground-länk till kod


Uppdatering: TypeScript 4.0 introducerade variadiska tuplet-typer, vilket innebär att Last nu kan implementeras enkelt som

type Last<T extends any[]> = T extends [...infer I, infer L] ? L : never; 

Och resten fungerar som vanligt:

type LastParameter<F extends (...args: any)=>any> = Last<Parameters<F>>;
type somefntype = (a: number, b: string, c: boolean) => void
type lastparamtype = LastParameter<somefntype> // boolean

Playground-länk till kod


TypeScript 3.0 introducerade tuplet-typer i rest- och spread-uttryck, specifikt för att möjliggöra programmerbar konvertering mellan typerna av funktionsparameterlistor och tupler. Det finns ett typalias som kallas Parameters<F> definierat i standardbiblioteket som returnerar parameterlistan för en funktion som en tuppel.

Det är irriterande men möjligt att få det sista elementet i en tuple-typ med en fast men godtycklig längd:

// Tail<T> returns a tuple with the first element removed
// so Tail<[1, 2, 3]> is [2, 3]
// (works by using rest tuples)
type Tail<T extends any[]> = 
  ((...t: T)=>void) extends ((h: any, ...r: infer R)=>void) ? R : never;

// Last<T> returns the last element of the tuple
// (works by finding the one property key in T which is not in Tail<T>)
type Last<T extends any[]> = T[Exclude<keyof T, keyof Tail<T>>];

Genom att sätta ihop dem får du

type LastParameter<F extends (...args: any)=>any> = Last<Parameters<F>>;

Och vi kan testa det:

type somefntype = (a: number, b: string, c: boolean) => void
type lastparamtype = LastParameter<somefntype> // boolean

Ser bra ut för mig. Länk till koden

Svar #2

Här är ännu en lösning som är en liten modifiering av jcalz svar för att göra det mer generiskt med hjälp av några rekursiva villkorstyper:

type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
type Tail<T extends any[]> =
    ((...t: T) => any) extends ((_: any, ...tail: infer U) => any)
        ? U
        : [];
type HasTail<T extends any[]> = T extends ([] | [any]) ? false : true;

type Last<T extends any[]> = {
    0: Last<Tail<T>>
    1: Head<T>
}[
    HasTail<T> extends true ? 0 : 1
];

type LastType = Last<Parameters<somefntype>>; // boolean

Det kan vara intressant att överväga att ersätta any med unknown också.

Relaterade artiklar