Comments 29
Для меня вся суть Typescript заканчивается на этом примере.
ПС: А flow такое ловит :)
Замечательно: вот вам инструмент по типизации, но он будет работать только если в классах будут поля. Я неслучайно добавил последнюю строку в пример: instanceof из самого обычного JS почему-то и без полей прекрасно знает, что это объекты разных типов, а вот "типизированный" TS — нет
Проблема в инструменте, который обещает типизацию, а на деле работает как JS. Разработчик понадеется на такой инструмент и попадет впросак. Одинаковый интерфейс в контексте JS — это просто одинаковые названия методов. В итоге можно передать объект не того типа, с совсем другой логикой и никто не обратит на это внимания.
Действительно, в чем проблема если мы можем передать в функцию что угодно, что совпадает по структуре? Очевидно, что надо проявлять бдительность, чтобы не проморгать такую ситуацию.
Выполнение этого кода безопасное и будет всегда
false
Это нифига не безопасный пример и код. Представим себе, что у нас там не Cat и Dog, а Date и Disk, и у каждого по методу format. Нужно пояснять, что произойдет?
function lol(date: Date) {
console.log(date.format());
}
lol(new Disk())
Придётся представить, что у Date и Disk общий предок, а я не могу себе такое представить.
Это неудачный пример, у вас могут быть две разные сущности, которые имеют тип {id:number, name:string}
и два соотв. сервиса чтобы их сохранять на бэкенде, компилятор разрешит вам сохранить любую сущность любым сервисом, что будет ошибкой. Люди не знакомые с тайпскриптом пытаются использовать маркерные (пустые) интерфейсы, что тоже приводит к похожим ошибкам.
1. Мой ответ был к примеру кода где в минус было приписано что код
console.log(new Cat() instanceof Dog)
Должен почему-то быть не валидным с точки зрения анализатора, но это не так.
Ну попробуйте сделать тоже самое например в C# (там вы получите уведомление что условие всегда будет ложным)
2. Приведенный вами пример будет не валидным. Предлагаю вам попробовать не в онлайн редакторе
The basic rule for TypeScript’s structural type system is that x is compatible with y if y has at least the same members as x.
Для того, чтобы ваш код был валидным, нужно что бы в классе Disk как минимум были объявлены поля и методы с теми же типами, что и у Date.
Меня например смущает в C#, что необходимо явно указывать интерфейс, даже если структура ему полностью соответствует
interface INamed {
name: string;
}
class MyClass {
public name: string: 'My Class name';
}
function logName(data: INamed): void {
console.log(data.name);
}
logName(new MyClass()); // В язык с номинативной типизацией компилятор будет ругаться
В тайпскрипт такой проблемы не будет, но! Эта вся история работает в обе стороны, что указано в твоём примере, не думаю что это проблема конкретно тайпскрипта:
class MyClass {
public name: string = 'My Class name';
}
function processMyClass(data: MyClass): void {
//
}
processMyClass({name: 'another string'});
И да, как писали выше решается это через что-то вроде
private readonly __classGuard: 'MyClass' = 'MyClass';
В тайпскрипт такой проблемы не будет, но!
Это не проблема — это прямое нарушение логики. Правильно будет так:
interface INamed {
name: string;
}
class MyClass implements INamed {
public name: string: 'My Class name';
}
И никаких проблем, как и нарушений логики не будет.
И да, как писали выше решается это через что-то вроде private readonly __classGuard: 'MyClass' = 'MyClass';
Это костыль, причём работоспособность его крайне сомнительна. Как минимум класс можно засунуть в неймспейс, либо импортировать под другим именем.
не думаю что это проблема конкретно тайпскрипта:
Проблема. Как минимум это ничего не даёт. А реально всё ломает, ведь в языке есть рефлекшн, декораты и много чего ещё, что позволяет как угодно менять кишки. И эта структурная типизация просто глупость, причём очень необоснованная глупость.
1. Способ создания объекта через new.
2. Тип, который является по сути интерфейсом.
Поэтому можно писать:
class C { }
interface I extends C {
x: number;
}
В вашем случае все типы структурно эквиваленты:
class C { }
interface I extends C { }
class D implements I { }
function f(o: {});
function f(d: D);
function f(i: I);
function f(c: C): void { // {} = D = I = C
}
И в итоге приходим к тому, что или в код пробивается некоторое количество any, или код будет снабжен интерфейсными «затычками», не делающими ничего полезного, кроме локализации any. И то и другое — прямо скажем, посредственные исходы.
function getBy(model, prop, value) {
return model.filter(item => item[prop] === value)[0]
}
Я бы заменил filter на find. И убрал бы [0].
Без необходимости, ни к чему ресурсы кушать. Да и кода меньше.
Школа магии TypeScript: дженерики и расширение типов