- # Поддержка TypeScript
- # Официальные декларации типов в npm-пакетах
- # Рекомендуемая конфигурация
- # Конфигурация Webpack
- # Инструменты для разработки
- # Создание нового проекта
- # Поддержка в редакторах
- # Объявление компонентов Vue
- # Использование с Options API
- # Расширение типов для globalProperties
- # Аннотация возвращаемых типов
- # Аннотация входных параметров
- # Аннотация событий
- # Использование с Composition API
- # Типизация ссылок на элементы шаблона
- # Типизация refs
- # Типизация reactive
- # Типизация computed
- # Типизация обработчиков событий
# Поддержка TypeScript
Vue CLI (opens new window) предоставляет встроенную поддержку TypeScript из коробки.
# Официальные декларации типов в npm-пакетах
Статическая система типов может помочь предотвратить многие потенциальные ошибки по мере роста приложения, поэтому Vue 3 написан на TypeScript. А значит, для использования TypeScript во Vue не нужны никакие дополнительные инструменты — он уже поставляется с отличной поддержкой.
# Рекомендуемая конфигурация
// tsconfig.json "compilerOptions": "target": "esnext", "module": "esnext", // использование более строгого вывода типов для свойств данных в `this` "strict": true, "jsx": "preserve", "moduleResolution": "node" > >
Обратите внимание, требуется включать strict: true (или хотя бы noImplicitThis: true , который является частью флага strict ), чтобы проверять this в методах компонента, иначе он всегда будет интерпретироваться как тип any .
# Конфигурация Webpack
При использовании пользовательской конфигурации Webpack ts-loader необходимо настроить для парсинга блоков во .vue файлах:
// webpack.config.js module.exports = . module: rules: [ test: /\.tsx?$/, loader: 'ts-loader', options: appendTsSuffixTo: [/\.vue$/], >, exclude: /node_modules/, >, test: /\.vue$/, loader: 'vue-loader', > .
# Инструменты для разработки
# Создание нового проекта
(opens new window) умеет генерировать новые проекты, которые будут использовать TypeScript:
# 1. Устанавливаем Vue CLI, если ещё не установлен npm install --global @vue/cli # 2. Создаём новый проект, затем выбираем опцию "Manually select features" vue create my-project-name # В существующий проект Vue CLI без TypeScript # можно добавить его поддержку с помощью плагина для Vue CLI: vue add typescript
Убедитесь, что для секции script компонента в качестве языка указан TypeScript:
Или при желании совмещать использование TypeScript с JSX render -функцией:
# Поддержка в редакторах
Для разработки приложений Vue на TypeScript настоятельно рекомендуем использовать Visual Studio Code
(opens new window) , обеспечивающее отличную поддержку TypeScript из коробки. При использовании однофайловых компонентов (SFC) также установите расширение Volar
(opens new window) , которое добавит вывод типов TypeScript в них и множество других отличных возможностей.
(opens new window) из коробки поддерживают как TypeScript, так и Vue. Другие IDE от JetBrains также имеют поддержку из коробки или через бесплатный плагин
# Объявление компонентов Vue
Чтобы TypeScript правильно определял типы внутри опций компонентов Vue необходимо объявлять компоненты с помощью глобального метода defineComponent :
import defineComponent > from 'vue' const Component = defineComponent( // вывод типов будет работать >)
При использовании однофайловых компонентов это будет выглядеть так:
script lang="ts"> import defineComponent > from 'vue' export default defineComponent( // вывод типов будет работать >) script>
# Использование с Options API
TypeScript умеет выводить большинство типов без их явного определения. Например, если есть компонент со свойством count , то будет выведена ошибка при попытке вызывать строковый метод на этом свойстве:
const Component = defineComponent( data() return count: 0 > >, mounted() const result = this.count.split('') // => Property 'split' does not exist on type 'number' > >)
К сложным типам или интерфейсам можно приводить с использованием type assertion
interface Book title: string author: string year: number > const Component = defineComponent( data() return book: title: 'Руководство по Vue 3', author: 'Команда Vue', year: 2022 > as Book > > >)
# Расширение типов для globalProperties
Для добавления глобальных свойств, доступных в любом экземпляре компонента, Vue 3 предоставляет объект globalProperties . Например, для плагина может потребоваться внедрить глобальный объект или функцию.
import axios from 'axios' import createApp > from 'vue' const app = createApp(>) // Пользовательское объявление глобального свойства app.config.globalProperties.$http = axios // Плагин для валидации некоторых данных export default install(app, options) app.config.globalProperties.$validate = (data: object, rule: object) => // проверка, что объект соответствует определённым правилам > > >
Сообщить TypeScript об этих новых свойствах можно используя расширение модуля
Для примера выше потребовалось бы добавить следующее объявление типа:
import axios from 'axios' declare module '@vue/runtime-core' export interface ComponentCustomProperties $http: typeof axios $validate: (data: object, rule: object) => boolean > >
Объявление типа можно указать в этом же файле или в общем файле *.d.ts всего проекта (например, в src/typings , чтобы он автоматически загружался TypeScript). Разработчикам библиотек и плагинов нужно указать этот файл в файле package.json в свойстве types .
Убедитесь, что файл декларации является модулем TypeScript
Чтобы воспользоваться преимуществами расширения модуля, нужно убедиться что в файле будет хотя бы один import или export корневого уровня, даже если это будет просто export <> .
Любой файл с import или export корневого уровня рассматривается как модуль в TypeScript
(opens new window) . Если объявление типов сделано вне модуля, то они перезапишут исходные типы, а не расширят их.
Подробнее о типе ComponentCustomProperties смотрите в его определении в @vue/runtime-core
# Аннотация возвращаемых типов
Из-за цикличной природы файлов деклараций Vue, TypeScript может испытывать трудности с выводом типа вычисляемых свойств. По этой причине может потребоваться аннотировать возвращаемый тип вычисляемых свойств.
import defineComponent > from 'vue' const Component = defineComponent( data() return message: 'Привет' > >, computed: // требуется аннотация типа greeting(): string return this.message + '!' >, // при использовании сеттера, нужна аннотация для геттера greetingUppercased: get(): string return this.greeting.toUpperCase() >, set(newValue: string) this.message = newValue.toUpperCase() > > > >)
# Аннотация входных параметров
Входные параметры по указанному type проверяются во время выполнения. Чтобы передать эти типы в TypeScript потребуется приводить конструктор с помощью PropType :
import defineComponent, PropType > from 'vue' interface Book title: string author: string year: number > const Component = defineComponent( props: name: String, id: [Number, String], success: type: String >, callback: type: Function as PropType() => void> >, book: type: Object as PropTypeBook>, required: true >, metadata: type: null // metadata будет с типом any > > >)
(opens new window) TypeScript, когда дело доходит до вывода типов выражений функций, необходимо быть осторожным со значениями validator и default для объектов и массивов:
import defineComponent, PropType > from 'vue' interface Book title: string year?: number > const Component = defineComponent( props: bookA: type: Object as PropTypeBook>, // Убедитесь, что используете стрелочную функцию default: () => ( title: 'Выражение со стрелочной функцией' >), validator: (book: Book) => !!book.title >, bookB: type: Object as PropTypeBook>, // Или явно указывайте this параметром default(this: void) return title: 'Выражение с функцией' > >, validator(this: void, book: Book) return !!book.title > > > >)
# Аннотация событий
Можно объявить тип данных, передаваемых вместе с событием. Кроме того, все необъявленные в emits события при вызове будут выбрасывать ошибку:
const Component = defineComponent( emits: addBook(payload: bookName: string >) // валидации во время выполнения return payload.bookName.length > 0 > >, methods: onSubmit() this.$emit('addBook', bookName: 123 // Ошибка типа! >) this.$emit('non-declared-event') // Ошибка типа! > > >)
# Использование с Composition API
В функции setup() не требуется указывать типы для параметра props , так как они будут выводиться из опции props компонента.
import defineComponent > from 'vue' const Component = defineComponent( props: message: type: String, required: true > >, setup(props) const result = props.message.split('') // ОК, потому что 'message' строка const filtered = props.message.filter(p => p.value) // ОШИБКА: Property 'filter' does not exist on type 'string' > >)
# Типизация ссылок на элементы шаблона
Иногда может потребоваться аннотировать ссылку в шаблоне на дочерний компонент, чтобы вызвать его публичный метод. Например, есть дочерний компонент MyModal с методом, который открывает модальное окно:
import defineComponent, ref > from 'vue' const MyModal = defineComponent( setup() const isContentShown = ref(false) const open = () => (isContentShown.value = true) return isContentShown, open > > >)
И вызвать этот метод из родительского компонента через ссылку на элемент шаблона:
import defineComponent, ref > from 'vue' const MyModal = defineComponent( setup() const isContentShown = ref(false) const open = () => (isContentShown.value = true) return isContentShown, open > > >) const app = defineComponent( components: MyModal >, template: ` `, setup() const modal = ref() const openModal = () => modal.value.open() > return modal, openModal > > >)
Хоть это будет работать, но не будет никакой информации о типе MyModal и его доступных методах. Чтобы исправить это, нужно при создании ref-ссылки использовать InstanceType :
setup() const modal = refInstanceTypetypeof MyModal>>() const openModal = () => modal.value?.open() > return modal, openModal > >
Обратите внимание, что необходимо воспользоваться оператором optional chaining
(opens new window) или любым другим способом, чтобы проверять, что modal.value не будет undefined.
# Типизация refs
Ref-ссылки выводят тип из исходного значения:
import defineComponent, ref > from 'vue' const Component = defineComponent( setup() const year = ref(2022) const result = year.value.split('') // => Property 'split' does not exist on type 'number' > >)
Иногда может потребоваться указать сложный тип для внутреннего значения ref-ссылки. Это можно сделать передав общий аргумент при вызове ссылки для переопределения вывода типа по умолчанию:
const year = refstring | number>('2022') // тип year: Ref year.value = 2022 // ОК!
Если generic тип неизвестен, рекомендуется приводить ref к Ref .
# Типизация reactive
При типизации свойства reactive можно использовать интерфейсы:
import defineComponent, reactive > from 'vue' interface Book title: string year?: number > export default defineComponent( name: 'HelloWorld', setup() const book = reactiveBook>( title: 'Руководство по Vue 3' >) // ИЛИ const book: Book = reactive( title: 'Руководство по Vue 3' >) // ИЛИ const book = reactive( title: 'Руководство по Vue 3' >) as Book > >)
# Типизация computed
Вычисляемые свойства автоматически выводят тип из возвращаемого значения:
import defineComponent, ref, computed > from 'vue' export default defineComponent( name: 'CounterButton', setup() let count = ref(0) // только для чтения const doubleCount = computed(() => count.value * 2) const result = doubleCount.value.split('') // => Property 'split' does not exist on type 'number' > >)
# Типизация обработчиков событий
Работая с нативными событиями DOM может быть полезным типизировать аргумент, передаваемый в обработчик события. Например:
template> input type="text" @change="handleChange" /> template> script lang="ts"> import defineComponent > from 'vue' export default defineComponent( setup() // `evt` будет с типом `any` const handleChange = evt => console.log(evt.target.value) // TypeScript здесь выбросит ошибку > return handleChange > > >) script>
Как можно увидеть, без правильной типизации аргумента evt , TypeScript станет выбрасывать ошибку при попытке получить доступ к значению элемента . Решением этой проблемы будет приведение к правильному типу target события:
const handleChange = (evt: Event) => console.log((evt.target as HTMLInputElement).value) >
(opens new window)
Последнее обновление страницы: около 1 года назад