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å.
