Type narrowing (Type Narrowing) in TypeScript is het proces waarbij de compiler "begrijpt" dat een variabele in een bepaald stuk code een specifieker type heeft, op basis van voorwaarden.
Typische technieken voor type narrowing:
typeof operator:function example(x: number | string) { if (typeof x === 'string') { // x: string hier return x.toUpperCase(); } else { // x: number hier return x.toFixed(2); } }
instanceof (voor klassen):if (dateObj instanceof Date) { // dateObj: Date }
null en undefined:function print(value?: string) { if (value != null) { // value: string console.log(value.length); } }
type Pet = { kind: 'dog'; woof: () => void } | { kind: 'cat'; meow: () => void }; function sound(pet: Pet) { if (pet.kind === 'dog') { pet.woof(); } else { pet.meow(); } }
TypeScript ondersteunt ook gebruikersgedefinieerde type guard functies:
function isString(x: unknown): x is string { return typeof x === 'string'; }
Narrowing maakt typechecks veiliger en de code betrouwbaarder.
Kan type narrowing gegarandeerd worden door gewone vergelijkingen (bijvoorbeeld
==/===), en werkt dit altijd?
Antwoord: Nee. In veel gevallen begrijpt TypeScript het type niet uit eenvoudige vergelijkingen, vooral als de vergelijking te "onduidelijk" is of via indirecte variabelen/eigenschappen plaatsvindt. Voor narrowing moet vaak expliciete mechanismen gebruikt worden (typeof, instanceof, discriminante eigenschappen en type guards).
Voorbeeld:
function foo(x: number | string | null) { if (x) { // x: string | number, null is al onmogelijk, maar narrowing naar specifieker type zal er niet zijn } }
Verhaal
In een groot TypeScript-project heeft de conditie user.role == 'admin' het datatype niet genarrowd, en het was nog steeds nodig om controles op eigenschapsexistentie te schrijven. Ontwikkelaars onderschatten de narrowing regels, wat leidde tot fouten "Cannot read property ... of undefined".
Verhaal
In een mobiele applicatie accepteerde de functie ofwel een object of een string. Door een indirecte functie-aanroep die het type veranderde, vond er geen narrowing plaats, en op sommige apparaten was er een crash bij het aanroepen van een methode die niet bij een string hoorde. Testen voor zeldzame gevallen zijn mislukt.
Verhaal
Bij de migratie van code van JavaScript naar TypeScript werden onze type guard functies niet geïmplementeerd, in de veronderstelling dat controles op eigenschappen altijd het type zouden versmallen. Dit resulteerde in complexe objecten met optionele velden die zich niet correct gedroegen, en runtime veroorzaakte onvoorspelbare toegangsfouten naar gegevens.