exactOptionalPropertyTypes în TypeScript

O proprietate opțională (telefon?: string) poate lipsi, dar nu poate primi explicit valoarea undefined.

Sintaxă
"compilerOptions": { "exactOptionalPropertyTypes": true }

tsc --exactOptionalPropertyTypes

Ce face exactOptionalPropertyTypes

Pentru TypeScript, telefon?: string înseamnă implicit „string sau undefined, sau lipsește”. În JavaScript, o proprietate care lipsește nu este însă același lucru cu una care există și are valoarea undefined: "telefon" in obj, Object.keys și operatorul spread le tratează diferit.

Cu exactOptionalPropertyTypes, telefon?: string înseamnă doar „lipsește sau este string”. Ca să permiți și undefined scris explicit, declari email?: string | undefined.

Implicit este false și nu face parte din strict. Are nevoie de strictNullChecks. tsc --init din 5.9 o pornește în fișierul nou, la „Stricter Typechecking Options”.

Parametri

Parametru Descriere
true Proprietățile opționale nu acceptă undefined decât dacă îl scrii în tip.
false Valoarea implicită. p?: T acceptă și undefined.

Exemple

Exemplul 1: undefined pus explicit

telefon: undefined nu este permis, pentru că tipul spune doar string. Pentru email, tipul include undefined, deci b este corect.

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "exactOptionalPropertyTypes": true
  }
}

// index.ts
interface Client {
  nume: string;
  telefon?: string;
  email?: string | undefined;
}

const a: Client = { nume: "Ana", telefon: undefined };
const b: Client = { nume: "Ion", email: undefined };
console.log(a, b);

Rezultat (tsc 5.9)

$ npx tsc --noEmit
index.ts(7,7): error TS2375: Type '{ nume: string; telefon: undefined; }' is not assignable to type 'Client' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
  Types of property 'telefon' are incompatible.
    Type 'undefined' is not assignable to type 'string'.

Exemplul 2: De ce contează diferența

Cele două obiecte arată la fel când citești telefon, dar nu sunt la fel pentru in, Object.keys și spread: în al doilea, valoarea undefined suprascrie telefonul vechi.

interface Client {
  nume: string;
  telefon?: string | undefined;
}

const lipsa: Client = { nume: "Ana" };
const gol: Client = { nume: "Ana", telefon: undefined };

console.log(lipsa.telefon, gol.telefon);
console.log("telefon" in lipsa, "telefon" in gol);
console.log(Object.keys(lipsa), Object.keys(gol));

const vechi = { nume: "Ana", telefon: "0722 000 111" };
console.log({ ...vechi, ...lipsa }.telefon);
console.log({ ...vechi, ...gol }.telefon);

Rezultat

undefined undefined
false true
[ 'nume' ] [ 'nume', 'telefon' ]
0722 000 111
undefined

Greșeli frecvente

  • Să pornești opțiunea într-un proiect mare fără să te aștepți la multe erori: codul de forma { x: valoare ?? undefined } este foarte des întâlnit.
  • Să crezi că opțiunea schimbă ceva la rulare. Doar verificarea tipurilor se schimbă.

Comenzi înrudite

  • strictNullChecks: Face din null și undefined tipuri separate: o valoare care poate lipsi trebuie verificată înainte de folosire.
  • noUncheckedIndexedAccess: Adaugă undefined la tipul valorilor citite cu index (note[0], stoc[„pere”]), pentru că ele pot lipsi.
  • --init: Creează un fișier tsconfig.json nou, cu setările recomandate de versiunea curentă de TypeScript.