Skip to content

TypeScript с Options API ​

На этой странице предполагается, что вы уже прочитали обзор по использованию Vue с TypeScript.

Совет

Хотя Vue поддерживает использование TypeScript с помощью API Options, рекомендуется использовать Vue с TypeScript через API Composition, поскольку он предлагает более простой, эффективный и надежный вывод типов.

Типизация входных параметров компонента ​

Вывод типов для входных параметров в API Options требует обертывания компонента с помощью 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
  }
})

Однако runtime опция 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 Error: аргумент типа 'string' не может
    // быть присвоен параметру типа 'number'
    this.callback?.('123')
  }
})

Предостережения ​

Если версия TypeScript меньше 4.7, то следует быть осторожным при использовании значений функций для параметров проверки и параметров по умолчанию - обязательно используйте стрелочные функции:

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: 'Выражение стрелочной функции'
      }),
      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: 'Привет!'
    }
  },
  computed: {
    greeting() {
      return this.message + '!'
    }
  },
  mounted() {
    this.greeting // тип: string
  }
})

В некоторых случаях может потребоваться явная аннотация типа вычисляемого свойства, чтобы убедиться в корректности его реализации:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  data() {
    return {
      message: 'Привет!'
    }
  },
  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, если в tsconfig.json используются значения "strict": true или "noImplicitAny": true. Поэтому рекомендуется явно аннотировать аргумент обработчиков событий. Кроме того, может потребоваться явное приведение свойств к 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 module augmentation:

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 с Options APIУже загружено