microsoft / microsoft/TypeScript

Keyword to permit inferring a union for a type parameter

Offen
#44,312 7 Kommentare 6 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

In Discussion Suggestion
Vorherrschende Sprache
Go
Sterne
111k
Forks
14.4k
Ø Merge
1 T. 19 Std.
Gemergte PRs (30 T.)
117

Beschreibung

Suggestion

🔍 Search Terms

  • Widen
  • Generics
  • Open

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Right now TypeScript has some rules about when it decides to widen a generic type or not, which can produce confusing results for relatively similar function signatures:

declare function fn1<T>(items: Array<T>): T
declare function fn2<T>(items: Array<() => T>): T
declare function fn3<T>(items: Array<{ prop: T }>): T

let res1: number | string = fn1([42, "hi"])
let res2: number | string = fn2([() => 42, () => "hi"]) // string ("hi") is not a number
let res3: number | string = fn3([{ prop: 42 }, { prop: "hi" }]) // string ("hi") is not a number

Right now its entirely outside of your control what TypeScript will decide to do when a type parameter is not provided and what TypeScript does has changed a few times over the years.

It would help a lot if TypeScript gave you some more control over this behavior, to specify if you want your generics to widen or not.

Please ignore the syntax, just demonstrating where I'd expect it to go

// widen
declare function fn<widen T>(items: Array<() => T>): T;
fn([() => 1, () => 2, () => "three"]) // => number | string

// do not widen
declare function fn<donotwiden T>(items: Array<T>): T;
fn([1, 2, "three"]) // ERR

📃 Motivating Example

An increasingly popular way to use TypeScript is to produce types from runtime values, there are already lots of TypeScript features dedicated to making it easier to infer precise types from values, and there are even libraries like zod and io-ts to do things like:

let type = union(string(), number())

let value: unknown = ...
assert(value, type)
value // >> string | number

Libraries written in TypeScript are increasingly relying on TypeScript's value-to-type inference as part of their public API. So when TypeScript changes its rules about things like when to widen vs not widen, it can be much more dramatic of a breaking change that sometimes requires redesigning these libraries.

type Check<T> = (value: unknown) => value is T

function string(): Check<string> {...}
function number(): Check<number> {...}
function union<T>(...members: Assertion<T>[]): Check<T> {...}

let assert = union(string(), number()) // ERR: number is not a string
let assert = union<string | number>(string(), number())

Right now there are a couple hacks you can do to trick TypeScript into having the desired widening behavior (by lying about the actual types). But there is no guarantee that this behavior will stick around between TypeScript versions.

💻 Use Cases

type Check<T> = (value: unknown) => value is T

function string(): Check<string> {...}
function number(): Check<number> {...}
function union<widen T>(...members: Check<T>[]): Check<T> {...}

let assert1 = union(string(), number())
let assert2 = union<string | number>(string(), number())
type SomeUnionToStayInSyncWith = string | number
let assert3 = union<SomeUnionToStayInSyncWith>(string(), number())

[Playground]

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginne damit, die allgemeinen Beispiele zur Widening und das verknüpfte Playground zu reproduzieren, und lies anschließend das TypeScript-Verhalten bei der Typinferenz rund um generische Typparameter. Vergleiche die vorhandenen Ergebnisse für fn1, fn2, fn3 und union mit den vorgeschlagenen Fällen widen und donotwiden. Als erledigt gilt die Aufgabe, wenn eine abgestimmte Syntax und Semantik vorliegt und Compiler-Tests das angeforderte Inferenzverhalten abdecken.

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

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.