microsoft / microsoft/TypeScript
Consider disallowing `in` operator use with arbitrary key operands on closed types for which the keys are known
Personne n'a encore pris cette issue.
- Langage dominant
- Go
- Étoiles
- 111k
- Forks
- 14.4k
- Merge moyen
- 1 j 19 h
- PR mergées (30 j)
- 117
Description
🔍 Search Terms
in operator, type guard, known property/props, closed type, rename property
✅ Viability Checklist
- 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 isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
⭐ Suggestion
The in operator serves as a type guard against record types: https://github.com/Microsoft/TypeScript/issues/10485
As it is implemented, it allows checking for the presence of arbitrary keys on an object.
The suggestion would be to disallow using the in operator with key names that not one of the known keys in the object's type. This would prevent developer errors where the key for which the presence is checked is not one of the keys that was explicitly declared to the type system, and might for example contain a typo.
declare const foo: { someProp: number }
if ('a' in foo) { // type error: Property 'a' does not exist on type '{ someProp: number; }'.
...
}
This could be guarded behind a compiler option, so as to not introduce a breaking change.
Of course, the in operator has a wide range of uses, especially for type guarding property accesses on objects of completely unknown types, so these should be preserved:
declare const foo: unknown
declare const bar: Record<string, number>
declare const baz: { someProp: number; [k in string]: number }
// in allowed against unknown, any, and other "open" record types or types with an index signature
if ('a' in foo || 'a' in bar || 'a' in baz) {
...
}
For cases where the original type of the variable was not permissive enough, but the programmer knows better, we can allow checking for the presence of the key with an explicit cast:
const foo = { someProp: 123, a: 'hello' }
const bar: { someProp: number } = foo
if ('a' in bar as unknown) { // valid, with the explicit cast
bar // type is inferred the same as currently
// ^? { someProp: number } & { a: unknown; }
}
📃 Motivating Example
Currently, I can write a valid program to discriminate a union:
type Puppy = {
color: string
}
declare const foo: Puppy | { someProp: string }
if ('color' in foo) {
console.log(foo.color)
}
Now, let's say my British colleague has a pass at the code, renaming variable names to UK English:
type Puppy = {
colour: string // change made
}
declare const foo: Puppy | { someProp: string }
// elsewhere in the program
if ('color' in foo) { // change forgotten
console.log(foo.color)
}
the program remains valid, yet TypeScript is unable to provide any indication that the change had consequences.
With the proposed feature:
type Puppy = {
colour: string // change made
}
declare const foo: Puppy | { someProp: number }
// elsewhere in the program
if ('color' in foo) { // type error: Property 'color' does not exist on type 'Puppy | { someProp: number; }'.
console.log(foo.color)
}
if ('color' in foo as unknown) { // valid, with the explicit cast
console.log(foo.color)
}
💻 Use Cases
-
What do you want to use this for?
Preventing developer errors (typos), allowing for safe property renames. -
What shortcomings exist with current approaches?
Does not warn of checks for presence of properties which are unknown to the type system (to allow for checking of properties not represented in the type system). This is especially evident when using the operator to discriminate a union, as theinoperator key operand is always intended to be a known property in such cases. -
What workarounds are you using in the meantime?
https://stackoverflow.com/questions/70670913/type-safe-in-type-guard
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Commencez par retracer le comportement existant du garde de type TypeScript pour l’opérateur in et comparez-le avec les exemples fermé, ouvert, unknown et d’union de l’issue. Déterminez comment doivent se comporter les vérifications de clés connues, les signatures d’index, la compatibilité avec les options du compilateur et les casts explicites ; le travail est terminé lorsque les diagnostics proposés préservent les cas valides listés et incluent une couverture du scénario motivant de renommage de propriété.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- javascript, typescript
- Domaine
- compilers
- Type d'issue
- Fonctionnalité
- Difficulté
- 5/5
- Temps estimé
- Plus d'une semaine
- Activité
- À l'abandon
- Clarté
- Plutôt claire
- Accessibilité débutants
- 35/100