Pull to refresh

Comments 29

Что за элементарщина, эта инфа достойна странички в справочнике по языку, а не целой статьи
так страничка есть, и если выбросить всю «воду» из статьи, то статья окажется гораздо меньше, чем та самая страничка :)
Добавьте классам хоть какие-нибудь поля и будет вам щастье

Замечательно: вот вам инструмент по типизации, но он будет работать только если в классах будут поля. Я неслучайно добавил последнюю строку в пример: instanceof из самого обычного JS почему-то и без полей прекрасно знает, что это объекты разных типов, а вот "типизированный" TS — нет

А в чем, собственно, проблема? Какая в принципе может быть разница Cat там передан или Dog, если у них один и тот же интерфейс?

Проблема в инструменте, который обещает типизацию, а на деле работает как JS. Разработчик понадеется на такой инструмент и попадет впросак. Одинаковый интерфейс в контексте JS — это просто одинаковые названия методов. В итоге можно передать объект не того типа, с совсем другой логикой и никто не обратит на это внимания.

Инструмент обещает структурную типизацию и ее же обеспечивает.

Для классов хорошо бы иметь номинальную типизацию. И, вроде, так и есть в ТС.

Действительно, в чем проблема если мы можем передать в функцию что угодно, что совпадает по структуре? Очевидно, что надо проявлять бдительность, чтобы не проморгать такую ситуацию.

UFO just landed and posted this here

Нет, не кастов не встречал. Но я и не очень много классов встречаю в последнее время, всё больше функции, функции.

А что вы хотели от валидного с точки зрения типизации примера
Выполнение этого кода безопасное и будет всегда 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. Приведенный вами пример будет не валидным. Предлагаю вам попробовать не в онлайн редакторе
www.typescriptlang.org/docs/handbook/type-compatibility.html

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';

Это костыль, причём работоспособность его крайне сомнительна. Как минимум класс можно засунуть в неймспейс, либо импортировать под другим именем.

не думаю что это проблема конкретно тайпскрипта:

Проблема. Как минимум это ничего не даёт. А реально всё ломает, ведь в языке есть рефлекшн, декораты и много чего ещё, что позволяет как угодно менять кишки. И эта структурная типизация просто глупость, причём очень необоснованная глупость.
В TypeScript слово class объявляет 2 вещи:
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
     
}
Тайпскрипт прекрасен, но на практике даже больше половины этой прекрасности вдребезги разбивается об суровую реальность окружения. То есть, всего стороннего, начиная от самой инфраструктуры ECMAScript — уже там есть случаи, когда со строгими типами не всё безоблачно. Да, есть @types, вот только никто не гарантирует, что файлы деклараций для того или иного фреймворка или либы будут написаны хорошо, то есть так, чтоб ими можно было пользоваться без всё возрастающего желания послать всё на три буквы и переписать типы на any.

И в итоге приходим к тому, что или в код пробивается некоторое количество any, или код будет снабжен интерфейсными «затычками», не делающими ничего полезного, кроме локализации any. И то и другое — прямо скажем, посредственные исходы.
Если найдёте ошибку в types, кто вам мешает открыть pr и наслаждаться жизнью дальше?
Дело не в ошибках, а в подходах. Скажем, видели ли вы @types/d3? Я вот честно пытался влезть в объявленные там типы и структуры, недельку примерно. Потом плюнул и написал прослойку, которая бы стыковалась с нашим проектом, не вызывая при этом приступы мигрени. А в d3 всё уходило-приходило через any.
function getBy(model, prop, value) {
    return model.filter(item => item[prop] === value)[0]
}

Я бы заменил filter на find. И убрал бы [0].
Без необходимости, ни к чему ресурсы кушать. Да и кода меньше.

UFO just landed and posted this here
UFO just landed and posted this here
Sign up to leave a comment.