Typescript spread-operator til type
Jeg prøver at definere en type, som får en funktionstype som en generisk parameter og returnerer en funktionstype, som er den samme som inputfunktionstypen, bortset fra at den har et ekstra argument til sidst:
type AugmentParam<F extends (...args: any[]) => any, ExtraParam> = F extends (
...args: infer Args
) => infer R
? (
...args: [
...Args,
ExtraParam
]
) => R
: never
Et eksempel på brugen:
type F = (x: number) => boolean
type F2 = AugmentParam<F, string> // (x: number, arg2: string) => boolean
...Args ser ud til ikke at virke, men hvis jeg ændrer det til noget som dette, virker det:
type AugmentParam<F extends (...args: any[]) => any, ExtraParam> = F extends (
...args: infer Args
) => infer R
? (
...args: [
Args[0],
Args[1] /* Spread doesn't work here, so it doesn't work for arbitrary number of arguments :( */,
ExtraParam
]
) => R
: never
Men det virker kun for et specifikt antal argumenter, og jeg er nødt til at definere en sådan type for hver n-ær funktion.
Svar #1
TypeScript kan ret nemt repræsentere foranstillet en type på en tuple-type, kaldet Cons<H, T> sådan her:
type Cons<H, T extends readonly any[]> =
((h: H, ...t: T) => void) extends ((...r: infer R) => void) ? R : never
type ConsTest = Cons<1, [2, 3, 4]>;
// type ConsTest = [1, 2, 3, 4]
Du kan bruge denne definition sammen med betingede aflgnede tuplertyper til at producere en Push<T, V> for at tilføje en type til slutningen af en tuple:
type Push<T extends readonly any[], V> = Cons<any, T> extends infer A ?
{ [K in keyof A]: K extends keyof T ? T[K] : V } : never
type PushTest = Push<[1, 2, 3], 4>;
// type PushTest = [1, 2, 3, 4]
men denne definition af Push er skrøbelig. Hvis T-tuplet har valgfrie elementer, eller hvis det kommer fra parameterlisten i en funktion, vil du bemærke, at compileren "flytter" de valgfrie markører og parameternavne ét element til højre:
type Hmm = (...args: Push<Parameters<(optStrParam?: string) => void>, number>) => void;
// type Hmm = (h: string | undefined, optStrParam?: number) => void
Parameternavne er egentlig ikke en del af typen, så selvom det er irriterende, påvirker det ikke den faktiske type. At tilføje et argument efter et valgfrie er... mærkeligt, så jeg er ikke sikker på, hvad den rigtige opførsel er der. Ikke sikker på, om dette er afgørende for dig, men vær advaret.
Under alle omstændigheder ville din AugmentParam se sådan ud:
type AugmentParam<F extends (...args: any[]) => any, ExtraParam> =
(...args: Extract<Push<Parameters<F>, ExtraParam>, readonly any[]>)
=> ReturnType<F>
og det virker (med de tidligere forbehold):
type F = (x: number) => boolean
type F2 = AugmentParam<F, string>
// type F2 = (h: number, x: string) => boolean
type F3 = AugmentParam<F2, boolean>
// type F3 = (h: number, h: string, x: boolean) => boolean
Okay, håber det hjælper. Held og lykke!
Link til kode
