Comments 14
as DemoGridDataво множестве мест не нивелирует преимущества строгой типизации? Вот подумайте, чтобы сделать каст, вам нужно знать во что кастить в том или ином случае, и так в каждом участке кода, ошибиться с таким кастом очень легко, создается видимость что типизация есть, когда в полноценном виде ее нет. То есть по хорошему типы для props и data нужно бы при их объявлении указать один раз и дальше никогда каст/as не делать. Вот vue-class-component пропаганирует подобный подход, но не ваш пример. И например, абстрактно, с дженериками это могло бы выглядеть как-то так
Vue.extend<DemoGridProps, DemoGridData>({...})
Кроме того, интерфейсы дают возможность повторного использования определений типов для входных и выходных данных Vue-компонент. Ведь тип входных данных одной компоненты часто совпадает с выходными данными другой.
Разве нельзя использовать интерфейсы совместно с vue-class-component (прямо в декораторе, вместо инлайн описания типа)?
PS если уж хочется делать явный каст, то почему не один раз, например так (и можно развить идею, вынести метод cast в базовый класс и сделать его generic, тогда DemoGridData/DemoGridProps фигурировали бы только одни раз в шапке класса как generic параметры):
cast() {
return {
$data: this.$data,
$props: this.$props,
} as {$data: DemoGridData, $props: DemoGridProps};
}
И далее использовать как-то так: const {$data, $props} = this.cast();
...
Вариант с интерфейсами удобен, когда не сам проектируешь компоненты, а сдираешь чужой js-код.
Кроме того, бывает полезно видеть результат компиляции из TypeScript в JavaScript без посредников. Декоратор несколько искажает картину.
Хорошо. Вы типизировали js код. А что делать с шаблонами? Ваша типизация никак не влияет на связи между компонентами. И это огромная проблема сейчас. Потому что это затрудняет рефакторинг, а так же просто может дать сломаться приложению в рантайме по недосмотру кого-то из разработчиков.
Да, лучше это, чем ничего. Но говорить, что это строгая типизация — это очень большая натяжка.
Например, те же проблемы есть в приложениях WPF или XamarinForms. XAML-файлы представления тоже можно ненароком сломать. И здесь тоже определенные ошибки проявятся только в рантайм.
На 100% эту проблему никто ещё не решил нормально. Поэтому рефакторинг объектов, которые участвуют в биндинге — везде вызывает затруднения.
На уровне привязки ts-кода Vue-компоненты к его шаблону особых проблем нет.
Обычно это решается соглашением по именам (для AppGrid templateId=«app-grid-template», для DemoGrid templateId=«demo-grid-template»).
На уровне привязки (биндинга) атрибутов компоненты к определенным элементам представления, естественно можно нарваться на ошибки в рантайм.
Тут только грамотное тестирование сможет помочь. По-моему, Vue.js ругается вполне осмысленно в случае отсутствия нужных тэгов или сломанного биндинга.
Но ведь строгая типизация, в любом случае, помогает улучшать код. Поэтому её надо применять в той части, в которой она работает.
При написании tutotial я не ставил себе задачу написать идеальный пример.
Я выбрал официальный пример известного автора: Evan You.
Затем постарался минимизировать изменения в исходном коде.
Чтобы не потерять узнаваемость.
Так что: "так вообще писать нельзя" — это к автору :).
Что касается кривости кода — посмотрите на цитаты от создателя Vue.js в начале статьи. В них содержится ответ.
Vue.js изначально был плохо заточен под TypeScript.
Например, дублирование определений props неизбежно даже при использовании vue-class-component.
Поэтому и приходится использовать "костыли".
Есть варианты "костылей", которые выглядят получше, чем использованный мной.
Описанный мной вариант с интерфейсами, мне лично, экономит массу времени и нервов при изучении потрохов самого Vue.js, а также "чужих" компонент, которые я подбираю для использования в своих приложениях.
Обычно сам пример мне не нужен, поэтому тратиться на него нет смысла.
Главное — быстро заставить работать подходящий пример использования "нужной" компоненты.
А потом ставишь точки остановки и шаришься по коду "нужной" компоненты и Vue.js.
В подобных применениях будет удобнее, как мне кажется, подход с явным определением интерфейсов для входных и выходных данных Vue-компоненты.
Не требуется "включать мозги" и разбираться во внутренней логике работы "чужого" примера.
Всё зависит от поставленных целей.
Кстати, первый пример, который я использую в этой статье, тоже практически официальный.
Переход с официального сайта typescriptlang.org, выбрать Vue.js.
Автор примера: DanielRosenwasser — Program Manager of TypeScript.
Развве может он писать на TypeScript криво?
Полностью с вами согласен. Имя автора ничего не гарантирует. Но зато, при сильных наездах, легче отмазаться :).
Насчет "явно не TypeScript":
Образец на vuejs.org, куда вы ссылаетесь — лохматого года, и явно не Typescript. Вы его портируете, и остальные лохматости оставляете как есть.
И я про тоже самое говорю:
На широких просторах Интернета можно найти массу качественных примеров и готовых приложений, использующих Vue.js. Но подавляющее большинство этих примеров написано на JavaScript.
Зачастую, при попытке причесать "лохматости" что-нибудь отваливалось. Поэтому я и стараюсь оставлять "как есть".
Официальный стайл-гайд требует, чтобы для фреймворка явно описывались типы свойств компонента. У вас — массив. Исправляем, делаем явно — и получаем дубликат описаний, то есть еще хуже.
Массив использовать опасно ещё по одной причине — у компилятора TypeScript частично отключается контроль типов.
Теоретически оба варианта определения props
, которые приведены ниже, должны работать одинаково. И это действительно так. Даже получаемый на выходе JavaScript код одинаковый.
...
export default Vue.extend({
template: '#demo-grid-template',
props: ['rows', 'columns', 'filterKey'],
//props: { rows: Array, columns: Array, filterKey: String },
...
});
Но обнаруживается неприятный баг-фича в тайпинге Vue.js.
Если props
определены как массив, то для компилятора TypeScript перечисленные свойства становятся легальными.
Например, компилятор спокойно скушает использование this.filterKey
.
Перечисленные в props
свойства даже в IntelliSense появляются при использовании VS2017.
А если определить props
явно с указанием типов, то компилятор будет материться на тот же this.filterKey
.
Вот такой баг-фича.
Была похожая проблема связать TS и Vue. Надо было написать свои однофайловые компоненты, и само приложение целиком на TS.
Ваш способ мне не нравится.
<template>
<div>
<hr/>
MyDebug:
<div class="row">
<div class="col-lg-6">
<pre>{{order }}</pre>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import {Component, Prop, Emit} from 'vue-property-decorator'
import {Order} from "models/Order";
@Component
export default class MyDebug extends Vue {
@Prop()
order: Order;
mounted() {
}
// https://alligator.io/vuejs/typescript-class-components/
// https://github.com/kaorun343/vue-property-decorator
}
</script>
<style lang="css" scoped>
</style>
Строгая типизация для приложений Vue.js на TypeScript