Skip to content

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, повинні автоматично виконувати ці доповнення у власних визначеннях типів.

Розміщення цього доповнення підпадає під ті самі обмеження, що й глобальні доповнення властивостей.

Також до вашої уваги:

TypeScript з опційним API has loaded