Skip to content

Vue, TypeScript и импортированные типы: что может пойти не так?

Когда вы используете defineProps в Vue с TypeScript, компилятор Vue запускает обратную генерацию типов: из интерфейсов, аннотаций и значений по умолчанию (default) создаются JS-конструкторыBoolean, String, Object и другие. Именно они потом используются в скомпилированном компоненте в рантайме.

Компилятор постарается сделать всё возможное, чтобы вывести эквивалентные параметры времени выполнения, основанные на аргументах типа. Но сработает это не всегда.

Проблема: неработающий Boolean при импорте

На момент vue@3.5.*, если типы пропов импортируются, а не определяются прямо в компоненте — Vue может некорректно вывести тип Boolean при использовании shorthand-синтаксиса.

Пример

vue
<!-- Comp.vue -->
<script setup lang="ts">
import { Props } from "./types";

withDefaults(defineProps<Props>(), {
  disabled: false,
  sampleBooleanProp: false
});
</script>
ts
// types.ts
import { InputHTMLAttributes } from "vue";

export interface Props {
  disabled?: InputHTMLAttributes["disabled"] // импортированный тип
  sampleBooleanProp?: boolean
}

Результат

html
<!-- Пропы не указаны. Должны примениться значения по умолчанию -->
<Comp/>
<!-- ✅ Работает верно. disabled: false, sampleBooleanProp: false -->

<!-- Пропы указаны со значениями. Должны примениться значения true -->
<Comp :disabled="true" :sampleBooleanProp="true"/>
<!-- ✅ Работает верно. disabled: true, sampleBooleanProp: true -->

<!-- Пропы указаны со значениями. Должны примениться значения false -->
<Comp disabled="false" sampleBooleanProp="false"/>
<!-- ✅ Работает верно. disabled: false, sampleBooleanProp: false -->

<!-- Пропы указаны в shorthand-формате. Должны примениться значения true -->
<Comp disabled sampleBooleanProp/>
<!-- ❌ Ошибка. disabled: '' (пустая строка), sampleBooleanProp: true -->

Как видно из примера — если не передавать в проп disabled значение, то вместо ожидаемого true, проп равен пустой строке. То есть Vue не сгенерировал конструктор Boolean для disabled — он не смог вывести правильный тип из импортированного интерфейса.

Решение

Если поменять тип на локально объявленный, всё работает верно.

ts
// types.ts
// локальный интерфейс, полностью идентичен InputHTMLAttributes
interface InputHTMLAttributes_Local { disabled: boolean | "true" | "false" }

export interface Props {
  disabled?: InputHTMLAttributes_Local["disabled"] // локальный аналог
  sampleBooleanProp?: boolean
}

В таком случае TypeScript передаёт в Vue AST более полную информацию о локальных типах, и Vue корректно использует конструктор Boolean.

Проверить самостоятельно

Если хотите поэкспериментировать: Открыть демо на Vue Playground

Итог

Если вы столкнулись с тем, что shorthand-пропы ведут себя странно — вероятно, проблема в неправильно выведенном типе из импортированного интерфейса. Пока единственное решение — объявить типы локально или отказаться от shorthand-синтаксиса.

Если вам известно другое решение — пишите, будет интересно разобраться!