typescript - Evitați proprietățile suplimentare


Am o problemă cu comportamentul „Verificărilor de proprietăți în exces” din TypeScript. Aș dori să fiu sigur că un obiect cu proprietăți suplimentare nu este acceptat de TypeScript.

În exemplul meu de interfață simplă aș putea pur și simplu să aleg datele disponibile, dar am multe proprietăți și aș vrea să evit filtrarea lor la rulare, există vreo modalitate?

type LayoutType {
    margin: number;
}

const badData = {
    margin: 23,
    padding: 23,
}

function func(param: LayoutType) {
    console.log(param);
    // Here I want to use property being sure param only contains LayoutType property
}

// OK
func({ margin: 42 })
// OK : padding is detected as unwanted property
func({ margin: 42, padding: 32 })
// KO : bad data shouldn't fit
func(badData)

/* SAME */

// OK : padding is detected as unwanted property
const test1: LayoutType = { margin: 42, padding: 32 };
// KO : bad data shouldn't fit
const test2: LayoutType = badData;

Teren de joacă

Răspuns #1

Se pare că vrei un tip Exact. Typescript nu vine cu unul, dar este ușor de creat:

type Exact<A, B> = A extends B
  ? B extends A
    ? A
    : never
  : never

Acest lucru spune practic că dacă și A extinde B și B extinde A, atunci tipurile sunt identice, niciunul nu este un subset sau superset al celuilalt. Deci ar trebui să permită acel tip. Dacă nu sunt identice, atunci tipul este never, ceea ce împiedică acel tip să fie permis.

Acum trebuie doar să îți faci funcția generică și să impui ca argumentul să fie exact de tipul corect:

function func<T>(param: Exact<T, LayoutType>) {
    console.log(param);
}
func(badData)
// Argument of type '{ margin: number; padding: number; }'
//   is not assignable to parameter of type 'never'.

Teren de joacă

Mai multe lecturi relevante aici în problema Typescript #12936

În cele din urmă, motivul pentru care un literal de obiect nu funcționează, dar o variabilă de obiect funcționează, este că literalul este construit pentru un anumit tip. TypeScript nu poate ști despre proprietățile suplimentare deoarece nu există informații de tip pentru acele proprietăți. Prin urmare, programul TypeScript nu poate folosi acele proprietăți, deoarece nu sunt declarate ca existând.

Totuși, atunci când obiectul este o variabilă și proprietățile suplimentare sunt cunoscute, atunci este un tip separat dar compatibil. Proprietățile suplimentare nu pot fi utilizate într-o funcție care acceptă doar tipul restrâns, dar în alte coduri care cunosc tipul mai larg proprietățile pot fi utilizate.

Acesta este motivul pentru care acest lucru este valid:

const obj1 = { a: 1, b: 2 }
const obj2: { a: number } = obj1

console.log(obj1.b) // b can still be accessed

Dar aceasta nu este:

const obj1: { a: number } = { a: 1, b: 2 }
//                                  ^ type error

Răspunsul #2

Motivul

func({ margin: 42, padding: 32 })

nu funcționează, dar

func(badData)

se datorează unui lucru numit „Verificarea proprietăților în exces” în TypeScript:

Literalele de obiect primesc un tratament special și supun verificării proprietăților în exces atunci când sunt atribuite altor variabile sau transmise ca argumente. Dacă un literal de obiect are proprietăți pe care „tipul țintă” nu le are, vei primi o eroare.