microsoft / microsoft/TypeScript

Consider disallowing `in` operator use with arbitrary key operands on closed types for which the keys are known

Abierto
#59,299 6 comentarios 2 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Awaiting More Feedback Suggestion
Lenguaje dominante
Go
Estrellas
111k
Forks
14.3k
Merge medio
1 d 19 h
PR fusionados (30 d)
117

Descripción

🔍 Search Terms

in operator, type guard, known property/props, closed type, rename property

✅ Viability Checklist
⭐ 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
  1. What do you want to use this for?
    Preventing developer errors (typos), allowing for safe property renames.

  2. 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 the in operator key operand is always intended to be a known property in such cases.

  3. What workarounds are you using in the meantime?
    https://stackoverflow.com/questions/70670913/type-safe-in-type-guard

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Empieza rastreando el comportamiento existente del guard de tipos de TypeScript para el operador in y compáralo con los ejemplos cerrado, abierto, unknown y de unión del issue. Determina cómo deben comportarse las comprobaciones de claves conocidas, las firmas de índice, la compatibilidad con las opciones del compilador y las conversiones explícitas; se considera terminado cuando los diagnósticos propuestos conservan los casos válidos enumerados e incluyen cobertura para el escenario motivador de renombrado de una propiedad.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
javascript, typescript
Área
compilers
Tipo de issue
Nueva funcionalidad
Dificultad
5/5
Tiempo estimado
Más de una semana
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
35/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.