Перейти к содержимому

Structural vs Nominal Typing

TS использует структурную типизацию: совместимость определяется формой, а не именем. На интервью просят: «Почему функция, ожидающая Point, принимает литерал с лишними полями через переменную, но не напрямую?» Красный флаг — путать с nominal typing (Java/C#) и не знать про excess property checks.

Duck typing на пальцах:

  interface Point { x: number; y: number }
┌─────────────────────────┐         ┌────────────────────────────────┐
│ shapeA = { x:1, y:2 }   │  ✅ ──▶  │ принимается как Point          │
└─────────────────────────┘         │ (имеет все нужные поля)        │
└────────────────────────────────┘
┌────────────────────────────────┐  ┌────────────────────────────────┐
│ shapeB = { x:1, y:2, z:3 }     │  │ принимается через переменную   │
│ (extra field z)                │──│ но НЕ напрямую (excess check)  │
└────────────────────────────────┘  └────────────────────────────────┘
┌─────────────────────────┐         ┌────────────────────────────────┐
│ shapeC = { x:1 }        │  ❌ ──▶  │ не Point: нет y                │
└─────────────────────────┘         └────────────────────────────────┘
interface Point { x: number; y: number }
function dist(p: Point) {
return Math.hypot(p.x, p.y);
}
// 1) Объект с теми же полями — ок
dist({ x: 3, y: 4 });
// 2) Лишнее поле напрямую — ошибка (excess property check)
// dist({ x: 3, y: 4, z: 0 });
// 3) Через переменную — лишние поля разрешены
const p = { x: 3, y: 4, z: 0 };
dist(p);

Классы тоже структурны: implements необязателен.

class Cat { name = 'Tom'; meow() {} }
class Robot { name = 'R2'; meow() {} }
const cats: Cat[] = [];
cats.push(new Robot()); // ок: совпадает структура

Итог: Совместимость = форма. Лишние поля при literal-присваивании ловит excess property check, но через переменную проходят.