microsoft / microsoft/TypeScript

`typeof` expressions should return an equivalent of a guard

Aperta
#37,868 1 commento 6 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

In Discussion Suggestion
Lingua principale
Go
Stelle
111k
Fork
14.3k
Merge medio
1g 19h
PR unite (30g)
117

Descrizione

Search Terms

typeof guard array filter

Suggestion

Currently, typeof expressions return a boolean as expected. However, they are also special in that they narrow the types of their operands. In a similar vein, we can use type guards as the return value of functions to narrow(or assert) the types of their parameters:

declare const broad: string | number | object;
if(typeof broad === 'number') {
    // broad is now a number, not a string nor an object
}

declare function isNumber(param: any): param is number;

if(isNumber(broad) {
  // broad is now a number, not a string nor an object
}

This is all fine, but suppose you write a very specific function:

// Current return type: boolean
// Wish it was `param is number`
const isNumber = (param: any) => typeof param === 'number'

if (isNumber(broad)) {
   // param is possibly a string or object?!
}

This is IMO a special case that should be handled; particularly, when a typeof expression(or any expression that narrows a parameter's type) is the return value of a function, TS should infer that the function is a guard.

Notice that, unlike the discussion in #6015, this issue is about the inferred type of a function which has a return value of a narrowing-expression. We could go further:

const isObject = (o: unknown) => typeof o === 'object';
// type for isObject == (o:unknown) => o is object

const isElement = (o: object) => o instanceof Element;
// type for isElement == (o:object) => o is Element

const unknownIsElement = (o: unknown) => isObject(o) && isElement(o);
// type for unknownIsElement == (o:unknown) => o is Element

const numberIsElement = (o:unknown) => isNumber(o) && isElement(o); // error: o is number so isElement can't be called

(by the way, I searched but couldn't find a similar one. If this is a duplicate I apologize!)

Use Cases

The most immediate use case is for array filter:

// error: (string|object)[] not assignable to string[]
const stringElements: string[] = elements.filter(e => typeof e === 'string');

// Works, but repetitive
const stringElements: string[] = elements.filter((e): e is string => typeof e === 'string');

I'm sure this type of change would make guards far more powerful as well, since we could create functions combine JS-checks(typeof, instanceof) or even TS-checks(like discriminant variable checking) without having to explicitly write the guard expression(which, IMO is as good as a type assertion at the moment).

Examples

(see above)

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, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

This is possibly a breaking change, but IMO a good one as it would highlight actual programming errors for code that breaks

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia analizzando le aree del compilatore TypeScript responsabili dell’inferenza dei tipi di ritorno delle funzioni e del restringimento tramite controllo del flusso, usando gli esempi typeof, instanceof, composed-guard e Array.filter nell’issue come casi comportamentali. Il lavoro è completo quando i predicati inferiti restringono i valori come mostrato senza modificare il JavaScript emesso, e quando è coperto anche il comportamento di errore proposto.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
typescript
Ambito
compilers
Tipo di issue
Funzionalità
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
25/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.