Почему TypeScript не реализовал приведение типов, а только утверждение типа

Почему TypeScript не реализует приведение типов, а только утверждение типа? Я не ищу ответ для своего кода, а по той причине, что приведение типов не реализовано в TypeScript, и почему мы не должны (предположение!) реализовать его самостоятельно.

Пример. У меня есть интерфейс TypeScript, получающий JSON-данные от бэкэнда через AJAX-вызовы, имеющий элементы в элементах в элементах. Речь идет о еде, и вы должны платить цену, умноженную на час дня.

Мы получили этот JSON:

{
    "food" : [{
            "name" : "pizza",
            "price" : 1.234
            "ingredients" : [
                "name" : "cheese",
                "extra_price" : 1.2345
            ]
        }
    ]
}

И у нас есть эти классы:

class Food {

    public name : string;
    public price : number;
    public ingredients : Ingredient[];

    public timePrice() : number {
        return this.price * (new Date()).getHours();
    }
}

class Ingredient {
    public name : string;
    public extra_price : number;
}

Если мы приведем эти типы с помощью TypeScript, мы сможем прекрасно использовать свойства, включая ингредиенты. Отлично.

Но: Мы не можем использовать функцию timePrice. Потому что TypeScript выполняет утверждение типа, а НЕ приведение типов.

Я знаю вариант, что вы можете написать конструктор, создающий класс, когда вы передаете свойства в качестве параметров, но если у вас есть элементы в элементах в элементах: не вариант. Поэтому единственный вариант исправить это для меня — создать класс Utils со статической функцией и параметрами Food и Ingredient. Это работает.

Но почему? В чем проблема с приведением типов, что оно не реализовано в TypeScript? Мне кажется, это не так сложно построить, но поскольку MicroSoft этого не делала, должны быть непреодолимые проблемы при этом.


person user2425360    schedule 11.03.2017    source источник
comment
Приведение типов — это не то, что вы думаете. При приведении типов все, что у вас будет, — это исключение времени выполнения, говорящее о том, что объект JSON не является экземпляром Food. Что вам нужно, так это демаршалинг/привязка JSON к классам. Это будет роль библиотеки, а не языка (особенно языка, который должен быть переведен в JavaScript и работать без какой-либо конкретной библиотеки времени выполнения).   -  person JB Nizet    schedule 11.03.2017
comment
@JBNizet на 100% попал в точку. Я бы добавил, что даже в языках, которые поддерживают это напрямую, обычно не принято добавлять существенное поведение к конструкциям передачи/моделей данных. Поэтому, хотя OP может использовать библиотеку, я бы не рекомендовал ее. Лучше всего держать функциональность вне этих структур.   -  person Aluan Haddad    schedule 11.03.2017
comment
также имейте в виду, что typescript не добавляет никаких проверок во время выполнения. это волшебство только во время компиляции. Это также одна из причин, по которой в Typescript нет приведения типов.   -  person toskv    schedule 11.03.2017


Ответы (1)


Для объекта верхнего уровня с примитивными свойствами, такого как тот, что у вас есть здесь, вы, безусловно, можете просто изменить прототип объекта, чтобы он указывал на прототип класса, и большинство вещей будет работать так, как вы ожидаете.

Но как насчет такого класса?

class Food extends Something {
   name: string;
   price: number;
   eat = () => { super.consume(); };
}

Здесь каждый раз, когда мы вызываем new Food(), мы получаем новое замыкание для свойства eat. Теперь приведение во время компиляции к Food должно создать новое замыкание, каким-то образом найти правильную ссылку на метод consume суперкласса (это не тривиально — он может быть недоступен из текущей области видимости! ), перепривяжите его (это невозможно сделать со 100% точностью в ES6) и скопируйте его. Итак, есть одна проблема.

Становится хуже. Допустим, у вас есть такая группа классов:

class ShoppingCart {
  items: Array<Food | Sundry>;
}
class Food {
  name: string;
  calories?: number;
  eat() { }
}
class Sundry {
  name: string;
  brand?: string;
  use() { }
}

Затем вы пишете

var x: ShoppingCart = <ShoppingCart>{ items: [
  { name: 'ace' },
  { name: 'avocado', calories: 130 },
  { name: 'triscuits', brand: 'kraft', calories: 100 }
]};

Это становится хуже! Теперь нам нужно возиться с корневым объектом, и повторять объекты, чтобы установить их прототипы. Это становится действительно сложным. Но это еще хуже... элемент { name: 'ace' } является одновременно допустимым Food и допустимым Sundry! На что мы его бросаем? И элемент triscuits тоже неоднозначный — что нам делать?

И это еще хуже - у нас может не быть никакого способа получить ссылку времени выполнения на Food или Sundry из этого кода. Возможно, вы импортировали только ShoppingCart из какого-то модуля и не импортировали Food или Sundry.

Мы до сих пор не рассмотрели такие проблемы, как

  • Как мы запускаем инициализаторы из окружающих классов?
  • Как насчет частных и защищенных членов?
  • Как насчет классов, которые выполняют важную логику в своих конструкторах?
  • Как геттеры и сеттеры взаимодействуют с этим?
person Ryan Cavanaugh    schedule 13.03.2017