Pull to refresh

Comments 14

Сразу скажу с Vue не работаю, но сказать что имею. Разве явный каст
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 код. А что делать с шаблонами? Ваша типизация никак не влияет на связи между компонентами. И это огромная проблема сейчас. Потому что это затрудняет рефакторинг, а так же просто может дать сломаться приложению в рантайме по недосмотру кого-то из разработчиков.
Да, лучше это, чем ничего. Но говорить, что это строгая типизация — это очень большая натяжка.

Надежная связь между ts-кодом компоненты и его представленим (шаблоном) — это частный случай общей проблемы поддержания взаимосвязей между моделью и представлением.

Например, те же проблемы есть в приложениях WPF или XamarinForms. XAML-файлы представления тоже можно ненароком сломать. И здесь тоже определенные ошибки проявятся только в рантайм.

На 100% эту проблему никто ещё не решил нормально. Поэтому рефакторинг объектов, которые участвуют в биндинге — везде вызывает затруднения.

На уровне привязки ts-кода Vue-компоненты к его шаблону особых проблем нет.
Обычно это решается соглашением по именам (для AppGrid templateId=«app-grid-template», для DemoGrid templateId=«demo-grid-template»).

На уровне привязки (биндинга) атрибутов компоненты к определенным элементам представления, естественно можно нарваться на ошибки в рантайм.

Тут только грамотное тестирование сможет помочь. По-моему, Vue.js ругается вполне осмысленно в случае отсутствия нужных тэгов или сломанного биндинга.

Но ведь строгая типизация, в любом случае, помогает улучшать код. Поэтому её надо применять в той части, в которой она работает.
Вообще-то в TSX решили еще в первой версии TypeScript. И в шаблонах ангуляра вроде тоже.
Про TSX и ангруляр ничего не скажу.

К этому ещё можно добавить Razor-рендеринг в приложениях Asp.Net Core MVC.
Razor-синтаксис позволяет жестко связать модель и представление.
Но здесь одно лечат, другое — калечат.
Очень хорошо, что кто-то пишет на русском про TS применительно к Vue.js. Но остальное…

Про «строгую» типизацию в TS, где есть any и нет soundness — вам уже сказали.

Построение — мне лично кажется что class MyComponent extends FrameworkName {… } — семантически кривой конструкт. Сравните с:
class MyComponent extends React.Component {… }

Извините, но от кода просто кровь из глаз. Как будто ES6 прошел мимо, к чему такая многословность?

Сам Vue.js очень «волшебный» фреймворк, а вы ему еще магии добавляете. Официальный стайл-гайд требует, чтобы для фреймворка явно описывались типы свойств компонента. У вас — массив. Исправляем, делаем явно — и получаем дубликат описаний, то есть еще хуже.

Вот так вообще писать нельзя, динамически меняющиеся свойства статикой упихиваем в data секцию:
data: function () {
        var sortOrders: any = {};
        (this.$props as DemoGridProps).columns.forEach(function (key) {
            sortOrders[key] = 1
        })
        return {
            sortKey: '',
            sortOrders: sortOrders
        } as DemoGridData
    }


Для этой цели есть секция computed.

При написании 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 криво?

А что, имя автора кода как-то гарантирует, что конкретный его код — хороший? Если он не очень знаком с фреймворком, ожидаемо что он может писать криво. Сразу из стартера — общепринято бить по рукам, если data является объектом, а не функцией, возвращающей объект. Впрочем остальное в стартере выглядит опрятно.

Образец на vuejs.org, куда вы ссылаетесь — лохматого года, и явно не Typescript. Вы его портируете, и остальные лохматости оставляете как есть. Причём там есть извиняющая причина (не факт, что браузер свежий), а у вас её нет (т.к. компилятор TS обязателен).

К слову, Эван, при всём к нему уважении, зачастую пишет/выкладывает невменяемый код. С документированием кода во всей Vue.js экосистеме (включая сам фреймворк) очень плохо всё. Как пример, можно смотреть на vuex. И сравнить его с redux. Оцените например как реализован vuex хелпер mapGetters (то, что вчера пришлось смотреть).

Полностью с вами согласен. Имя автора ничего не гарантирует. Но зато, при сильных наездах, легче отмазаться :).


Насчет "явно не 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-class-component.
Sign up to leave a comment.

Articles