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, но через переменную проходят.