TypeScript з опційним API
Ця сторінка передбачає, що ви вже ознайомились із Використання Vue з TypeScript.
TIP
Хоча Vue підтримує використання TypeScript з Опційним API, все ж рекомендується використовувати TypeScript з Композиційним API, оскільки він пропонує простіший, ефективніший та надійніший спосіб визначення типу.
Типізація властивостей компонента
Визначення типу реквізитів з Опційним API, вимагає обгорнення компонента за допомогою defineComponent(). При цьому Vue може визначити типи реквізитів на основі параметрів props, враховуючи додаткові параметри, такі як required: true і default:
ts
import { defineComponent } from 'vue'
export default defineComponent({
// визначення типу ввімкнено
props: {
name: String,
id: [Number, String],
msg: { type: String, required: true },
metadata: null
},
mounted() {
this.name // тип: string | undefined
this.id // тип: number | string | undefined
this.msg // тип: string
this.metadata // тип: any
}
})Однак параметри props під час виконання, підтримують лише використання конструктора функцій, в якості типу реквізиту - немає способу вказати складні типи, такі як об'єкти з вкладеними властивостями або сигнатури виклику функції.
Щоб анотувати складні типи реквізитів, ми можемо використовувати утиліту типу PropType:
ts
import { defineComponent } from 'vue'
import type { PropType } from 'vue'
interface Book {
title: string
author: string
year: number
}
export default defineComponent({
props: {
book: {
// надання більш конкретного типу для `Object`
type: Object as PropType<Book>,
required: true
},
// також можна анотувати функції
callback: Function as PropType<(id: number) => void>
},
mounted() {
this.book.title // string
this.book.year // number
// TS Помилка: аргумент типу "string" не можна
// призначити параметру типу 'number'
this.callback?.('123')
}
})Застереження
Якщо ваша версія TypeScript нижча за 4.7, ви повинні бути обережними, використовуючи функції для опцій реквізиту validator і default - обов'язково використовуйте стрілочні функції:
ts
import { defineComponent } from 'vue'
import type { PropType } from 'vue'
interface Book {
title: string
year?: number
}
export default defineComponent({
props: {
bookA: {
type: Object as PropType<Book>,
// Обов’язково використовуйте стрілочні функції, якщо ваша версія TypeScript нижча за 4.7
default: () => ({
title: 'Arrow Function Expression'
}),
validator: (book: Book) => !!book.title
}
}
})Це запобігає TypeScript від необхідності визначати тип this у цих функціях, що, на жаль, може призвести до помилки визначення типу. Це обмеження було в попередній версії, і тепер покращено в TypeScript 4.7.
Типізація випромінювань компонента
Ми можемо оголосити очікуваний тип корисного навантаження для випромінювача події, використовуючи об’єкт синтаксису опції emits. Крім того, усі неоголошені випромінювачі подій викликатимуть типову помилку:
ts
import { defineComponent } from 'vue'
export default defineComponent({
emits: {
addBook(payload: { bookName: string }) {
// виконати перевірку під час виконання
return payload.bookName.length > 0
}
},
methods: {
onSubmit() {
this.$emit('addBook', {
bookName: 123 // Помилка типу!
})
this.$emit('non-declared-event') // Типова помилка!
}
}
})Типізація обчислюваних властивостей
Обчислювана властивість визначає свій тип на основі свого значення при поверненні:
ts
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
message: 'Hello!'
}
},
computed: {
greeting() {
return this.message + '!'
}
},
mounted() {
this.greeting // тип: string
}
})У деяких випадках ви можете явно анотувати тип обчислюваної властивості, щоб переконатися, в правильності реалізації:
ts
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
message: 'Hello!'
}
},
computed: {
// явно анотувати тип повернення
greeting(): string {
return this.message + '!'
},
// анотування доступної для запису обчисленої властивості
greetingUppercased: {
get(): string {
return this.greeting.toUpperCase()
},
set(newValue: string) {
this.message = newValue.toUpperCase()
}
}
}
})Явні анотації також можуть знадобитися в деяких граничних випадках, коли TypeScript не може визначити тип обчисленої властивості через циклічні визначення.
Типізація обробників подій
При роботі з нативними подіями DOM може бути корисним правильно типізувати аргумент, який ми передаємо обробнику. Розгляньмо це на прикладі:
vue
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
methods: {
handleChange(event) {
// `event` неявно має тип "any".
console.log(event.target.value)
}
}
})
</script>
<template>
<input type="text" @change="handleChange" />
</template>Без анотації типу аргумент event неявно матиме тип any. Це також призведе до помилки TS, якщо "strict": true або "noImplicitAny": true використовуються в tsconfig.json. Тому рекомендується явно анотувати аргументи обробників подій. Крім того, вам може знадобитися явно надати властивості для event:
ts
import { defineComponent } from 'vue'
export default defineComponent({
methods: {
handleChange(event: Event) {
console.log((event.target as HTMLInputElement).value)
}
}
})Доповнення глобальних властивостей
Деякі плагіни інсталюють глобально доступні властивості для всіх екземплярів компонента за допомогою app.config.globalProperties. Наприклад, ми можемо інсталювати this.$http для отримання даних, або this.$translate для інтернаціоналізації. Щоб це добре працювало з TypeScript, Vue надає інтерфейс ComponentCustomProperties, призначений для доповнення за допомогою доповнення модуля TypeScript:
ts
import axios from 'axios'
declare module 'vue' {
interface ComponentCustomProperties {
$http: typeof axios
$translate: (key: string) => string
}
}Також до вашої уваги:
Розміщення доповнень типу
Ми можемо розмістити це доповнення типу у файлі .ts або у файлі *.d.ts для всього проєкту. У будь-якому випадку переконайтеся, що його включено в tsconfig.json. Для авторів бібліотек/плагінів цей файл слід вказати у властивості types у package.json.
Щоб скористатися перевагами доповнення модуля, вам потрібно буде переконатися, що доповнення розміщено в модулі TypeScript. Тобто файл має містити принаймні один import або export верхнього рівня, навіть якщо це просто export {}. Якщо доповнення розміщено за межами модуля, воно перезапише вихідні типи, а не доповнить їх!
ts
// Не працює, перезаписує вихідні типи.
declare module 'vue' {
interface ComponentCustomProperties {
$translate: (key: string) => string
}
}ts
// Працює правильно
export {}
declare module 'vue' {
interface ComponentCustomProperties {
$translate: (key: string) => string
}
}Доповнення користувацьких параметрів
Деякі плагіни, наприклад vue-router, забезпечують підтримку користувацьких параметрів компонента, таких як beforeRouteEnter:
ts
import { defineComponent } from 'vue'
export default defineComponent({
beforeRouteEnter(to, from, next) {
// ...
}
})Без належного доповнення типу, аргументи цього хука неявно матимуть тип any. Ми можемо доповнити інтерфейс ComponentCustomOptions, щоб підтримувати ці користувацькі параметри:
ts
import { Route } from 'vue-router'
declare module 'vue' {
interface ComponentCustomOptions {
beforeRouteEnter?(to: Route, from: Route, next: () => void): void
}
}Тепер параметр beforeRouteEnter буде правильного типу. Зауважте, що це лише приклад - добре типізовані бібліотеки, такі як vue-router, повинні автоматично виконувати ці доповнення у власних визначеннях типів.
Розміщення цього доповнення підпадає під ті самі обмеження, що й глобальні доповнення властивостей.
Також до вашої уваги:
