microsoft / microsoft/TypeScript
Function parameters and return type inference with infer keyword
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Go
- Sterne
- 111k
- Forks
- 14.3k
- Ø Merge
- 2 T. 4 Std.
- Gemergte PRs (30 T.)
- 132
Beschreibung
I saw a couple of similar issues, but it all provided very poor specification of the problem, so I decided to open my own issue.
Description
The further step in bringing nice fp declarations into TypeScript is adding function parameters and return type inference through infer keyword.
Lets look at some examples:
type F1 = <T extends number>(a: T, b: T) => T
// Now: [number, number]
// Propose: [1, 1]
type R1 = F1 extends ((a: 1, b: infer P) => infer R) ? [P, R] : never
Here we passed parameter a as 1 and expect:
- Type parameter
Tto be inferred as1 - Type
Pto be inferred asT->1 - Type
Rto be inferred asT->1 - Result to be inferred as
[P, R]->[1, 1]
type F2 = <T1 extends AnyTuple, T2>(a: T1, b: T2, c: Reverse<T1>) => [T1, T2]
// Now: [never, unknown, [AnyTuple, unknown]]
// Propose: [[3, 2, 1], unknown, [[1, 2, 3], unknown]]
type R2 = F2 extends (a: [1, 2, 3], b: infer P2, c: infer P1) => infer R ? [P1, P2, R] : never
Here we passed parameter a as [1, 2, 3] and expect:
- Type parameter
T1to be inferred as[1, 2, 3] - Type parameter
T2to be replaced withunknowntype because we provided no info to be able to infer it - Type
P2to be inferred asT2->unknown - Type
P1to be inferred asReverse<T1>->Reverse<[1, 2, 3]>->[3, 2, 1] - Type
Rto be inferred as[T1, T2]->[[1, 2, 3], unknown] - Result to be inferred as
[P1, P2, R]-> `[[3, 2, 1], unknown, [[1, 2, 3], unknown]]
type F3 = <T extends AnyTuple>(a: T, b: T['length']) => typeof b
// Now: never
// Propose: 3
type R3 = F3 extends ((a: [1, 2, 3], b: infer LEN) => any) ? LEN : never
Here we expect:
- Type parameter
Tto be inferred as[1, 2, 3] - Type LEN to be inferred as
T['length']->[1, 2, 3]['length']->3 - Result to be inferred as
LEN->3
Case of wrapping type checkers:
enum string_literal { string_literal = '' }
type StringLiteral<T> = T extends string ? string extends T ? string_literal : T : string_literal
type F4 = <T>(a: StringLiteral<T>, b: T) => T extends '' ? [] : T
// Now: all cases never
// Proposed: ['foobar', 'foobar']
type R4_1 = F4 extends ((a: 'foobar', b: infer P) => infer R) ? [P, R] : never
// Proposed: ['', []]
type R4_2 = F4 extends ((a: '', b: infer P) => infer R) ? [P, R] : never
// Proposed: [string_literal, string]
type R4_3 = F4 extends ((a: string, b: infer P) => infer R) ? [P, R] : never
Use cases
In other similar issues I saw only use cases of function parameters inference, so I'll extend it with return type inference cases.
The first example is a call function:
declare function call<P extends AnyTuple, F extends ((...args: P) => unknown)>(
params: P, func: F
): F extends (...args: P) => infer R ? R : never
// Got: [unknown, unknown]
// Expected: [1, 2]
const a = call([1, 2], <A, B>(a: A, b: B): [A, B] => [a, b])
Also it may be used to infer return types on each step of computing pipe and flow function versions with unlimited parameters count. I'm sure, there're much more cases where this feature may be applied, but I can't remember any more right now.
Util types used
type Empty = readonly []
type AnyTuple = ReadonlyArray<unknown> & { readonly 0: unknown } | Empty
type Tail<T extends AnyTuple> = ((...args: T) => any) extends (head: any, ...tail: infer R) => any
? R extends AnyTuple ? Readonly<R> : never : Empty
type Prepend<T extends AnyTuple, E> = ((e: E, ...args: T) => any) extends
(...args: infer R) => any ? R extends AnyTuple ? Readonly<R> : never : never
type _ReverseWith<T extends AnyTuple, Result extends AnyTuple> = {
0: _ReverseWith<Tail<T>, Prepend<Result, T[0]>>
1: Readonly<Result>
}[T extends Empty ? 1 : 0]
type Reverse<T extends AnyTuple> = _ReverseWith<T, Empty>
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne mit der Prüfung der Beispiele für bedingte Typen und der im Issue dokumentierten aktuellen und vorgeschlagenen Ergebnisse. Da keine Implementierungsdateien, Tests oder Einstiegspunkte genannt sind, gehört das Auffinden des relevanten Typinferenz-Subsystems und das Definieren kompatibler Tests zur Arbeit. Als abgeschlossen gilt die Arbeit, wenn die vorgeschlagenen Fälle zur Inferenz von Parameter- und Rückgabetypen die angegebenen Ergebnisse liefern.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- typescript
- Bereich
- compilers
- Issue-Typ
- Feature
- Schwierigkeit
- 5/5
- Geschätzter Aufwand
- Über eine Woche
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 25/100