Как убрать неявное приведение разных классов в Typescript, даже если у них одна сигнатура

class A {
    field: number;
}

class B {
    field: number;
}

function f(arg: A) {
    arg.field = 5
}

////

const a = new A()
const b = new B()

f(a)
f(b) // WHY???

Вот такой код проходит все проверки на typecheck, не помогает даже strict mode. Если поменять название переменной в одном из классов - будет выдавать ошибку, значит такое неявное приведение появляется, только если интерфейсы классов полностью совпадают. Может есть какая-то настройка компилятора для таких случаев? Как убрать такое неявное приведение?


Ответы (1 шт):

Автор решения: sailybra

В TypeScript используется структурная типизация, то есть типы считаются совместимыми, если структура описываемых ими объектов совпадает. Номинальная типизация (когда тип объекта зависит от того как он объявлен, а не от его непосредственной структуры) в тс не предусмотрена, однако возможно реализовать нечто похожее на неё. Для такого существует паттерн Branded Types (брендированные типы), он заключается в задании незначимых в рантайме, но отличимых при компиляции различий для типов.

Пример:

class A {
    field!: number & { __brand?: "A" };
}

class B {
    field!: number & { __brand?: "B" };
}

function f(arg: A) {
    arg.field = 5
}

////

const a = new A()
const b = new B()

f(a)
f(b) // Ошибка, типы поля field не совпадают

a.field = 5 // Присваивать обычные числа допустимо
const field: number = b.field // Как и присваивать в обычные числа

Однако, я бы не советовал вам злоупотреблять подобным. Лучше примите существование структурной типизации и задумайтесь, действительно ли есть разница какой из 2х классов придёт в вашу функцию.

→ Ссылка