选项式 API

为组件的 props 标注类型

  1. 选项式 API 中对 props 的类型推导需要用 defineComponent() 来包装组件,有了它 Vue 才可以通过 props 以及一些额外的选项,比如 required: truedefault 来推导出 props 的类型:
  2. 然而,这种运行时 props 选项仅支持使用构造函数来作为一个 prop 的类型——没有办法指定多层级对象或函数签名之类的复杂类型;
    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
      }
    })
    
  3. 可以使用 PropType 这个工具类型来标记更复杂的 props 类型;
    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: argument of type 'string' is not
        // assignable to parameter of type 'number'
        this.callback?.('123')
      }
    })
    

组合式 API

为组件的 props 标注类型

  1. 通过泛型参数来定义 props 的类型:

    <script setup lang="ts">
      interface Props {
        foo: string
        bar?: number
      }
    
      const props = defineProps<Props>();
    </script>
    
  2. 使用基于类型的声明时,会失去为 props 声明默认值的能力,这可以通过 withDefaults 编译器宏解决:

    <script lang="ts">
      export interface Props {
        msg?: string
        labels?: string[]
      }
    
      const props = withDefaults(defineProps<Props>(), {
        msg: 'hello',
        labels: () => ['one', 'two']
      });
    </script>
    

为组件的 emits 标注类型

  1. <script setup>emit 函数的类型标注也可以通过运行时声明或是类型声明进行;

    <script setup lang="ts">
      // 运行时
      const emit = defineEmits(['change', 'update']);
    
      // 基于类型
      const emit = defineEmits<{
        (e: 'change', id: number): void
        (e: 'update', value: string): void
      }>();
    </script>
    

为 ref() 标注类型

  1. ref 会根据初始化时的值推导其类型:

    import { ref } from 'vue';
    
    // 推导出的类型:Ref<number>
    const year = ref(2020);
    // => TS Error: Type 'string' is not assignable to type 'number'.
    year.value = '2020';
    
  2. 有时可能想为 ref 内的值指定一个更复杂的类型,可以通过使用 Ref 这个类型:

    import { ref } from 'vue';
    import type { Ref } from 'vue';
    
    const year: Ref<string | number> = ref('2020');
    year.value = 2020; // 成功!
    
  3. 或者,在调用 ref() 时传入一个泛型参数,来覆盖默认的推导行为:

    import { ref } from 'vue';
    
    // 得到的类型:Ref<string | number>
    const year = ref <string | number> ('2020');
    year.value = 2020; // 成功!
    
  4. 如果指定了一个泛型参数但没有给出初始值,那么最后得到的就将是一个包含 undefined 的联合类型:

    import { ref } from 'vue';
    
    // 推导得到的类型:Ref<number | undefined>
    const n = ref <number> ();
    

为 reactive() 标注类型

  1. reactive() 也会隐式地从它的参数中推导类型:

    import { reactive } from 'vue';
    
    // 推导得到的类型:{ title: string }
    const book = reactive({ title: 'Vue 3 指引' });
    
  2. 要显式地标注一个 reactive 变量的类型,可以使用接口:

    import { reactive } from 'vue';
    
    interface Book {
      title: string
      year?: number
    }
    const book: Book = reactive({ title: 'Vue 3 指引' });
    

为 computed() 标注类型

  1. computed() 会自动从其计算函数的返回值上推导出类型:

    import { ref, computed } from 'vue';
    
    const count = ref(0);
    // 推导得到的类型:ComputedRef<number>
    
    const double = computed(() => count.value * 2);
    // => TS Error: Property 'split' does not exist on type 'number'
    const result = double.value.split('');
    
  2. 还可以通过泛型参数显式指定类型:

    const double = computed <number> (() => {
      // 若返回值不是 number 类型则会报错
    })
    

为事件处理函数标注类型

  1. 在处理原生 DOM 事件时,应该为传递给事件处理函数的参数正确地标注类型:

    <template>
      <input type="text" @change="handleChange" />
    </template>
    
    <script setup lang="ts">
      function handleChange(event) {
        // `event` 隐式地标注为 `any` 类型
        console.log(event.target.value);
      }
    </script>
    
  2. 显式地为事件处理函数的参数标注类型,此外,可能需要显式地强制转换 event 上的属性:

    function handleChange(event: Event) {
      console.log((event.target as HTMLInputElement).value);
    }
    

为模板引用标注类型

  1. 模板引用需要通过一个显式指定的泛型参数和一个初始值 null 来创建:

    <template>
      <input ref="el" />
    </template>
    
    <script setup lang="ts">
      import { ref, onMounted } from 'vue';
      const el = ref<HTMLInputElement | null>(null);
    
      onMounted(() => {
        el.value?.focus();
      });
    </script>
    
  2. 为了严格的类型安全,有必要在访问 el.value 时使用可选链或类型守卫,这是因为直到组件被挂载前,这个 ref 的值都是初始的 null,并且在由于 v-if 的行为将引用的元素卸载时也可以被设置为 null

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

你好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 选项式 API
    1. 1.1. 为组件的 props 标注类型
  2. 2. 组合式 API
    1. 2.1. 为组件的 props 标注类型
    2. 2.2. 为组件的 emits 标注类型
    3. 2.3. 为 ref() 标注类型
    4. 2.4. 为 reactive() 标注类型
    5. 2.5. 为 computed() 标注类型
    6. 2.6. 为事件处理函数标注类型
    7. 2.7. 为模板引用标注类型