在单文件组件中使用 TypeScript,需要在 <script> 标签上加上 lang="ts" 的 attribute。当 lang="ts" 存在时,所有的模板内表达式都将享受到更严格的类型检查
小结:
<script lang="ts">
</script>
<script setup lang="ts">
</script>
当 script 中使用了 ts ,模板 template 在绑定表达式时也支持ts。
如果在表达式中不指名类型时,编译器会报警告提示。
正确写法
表达式指定类型
当使用 <script setup> 时,defineProps() 宏函数支持从它的参数中推导类型
<script setup lang="ts">
const props = defineProps({
name: String,
data:{
type: Object,
required: true
}
})
</script>
通过泛型来定义 Props
<script setup lang="ts">
interface MyProps {
phone: string | number,
name ?: string,
age : number | string
hobby: {
type: string,
required: true
}
}
const props = defineProps<MyProps>()
</script>
传递给 defineProps 的泛型参数必须是以下格式:
defineProps<{ /*... */ }>() 一个对象字面量
对同一个文件中的一个接口或对象类型字面量的引用:
interface Props {/* ... */}
defineProps<Props>()
当使用了 基于类型声明时,就失去了 默认值 能力。 Vue 提供了一个 Api 可以解决此问题, withDefaults 编译器哄解决。
withDefaults 可以提供默认值的类型检查
先来复习一下 基于运行时怎么 声明默认值 Props
const props = defineProps({
name: {
type: String,
default: () => '默认值'
},
data:{
type: Object,
required: true
}
})
基于运行时 Props 默认值写法:
interface MyProps {
phone: string | number,
name ?: string,
age : number | string
hobby: {
type: string,
required: true
}
}
const props = withDefaults(defineProps<MyProps>(),{
name:'海军',
phone: '123123123123'
})
当没有使用 <script setup> 时,想要类型检查,那么必须使用 defineComponent() 传入setup() 的 props 对象类型是从 props 选项中推导而来
import { defineComponent } from 'vue'
export default defineComponent({
props: {
message: String
},
setup(props) {
props.message // <-- 类型:string
}
})
因为 Vue 组件是单独编译的,编译器目前不会抓取导入的文件以分析源类型。我们计划在未来的版本中解决这个限制。
在 < script setup> 中 , 给emit 函数 类型标注 可以通过两种形式来标注
运行时声明写法
//运行时写法
const emit = defineEmits(['getData'])
emit('getData', {
code:200,
msg: "传入数据成功",
str:"我是子组件过来的数据"
})
类型声明写法
const emit = defineEmits<{
(e: 'getId', id: number): void
}>()
emit('getId',2)
基于类型的声明使我们可以对所触发事件的类型进行更细粒度的控制。
defineComponent() 也可以根据 emits 选项推导暴露在 setup 上下文中的 emit 函数的类型:
import { defineComponent } from 'vue'
export default defineComponent({
emits: ['change'],
setup(props, { emit }) {
emit('change') // <-- 类型检查 / 自动补全
}
})
有时我们想给 ref 指定一个 复杂类型时, 可以通过 Ref 类型 声明 或者 调用 ref 时 传入一个泛型参数 来覆盖默认推倒行为。
import type {Ref} from "vue"
const studentId: Ref< String | Number> = ref(231)
调用ref 时,传入一个泛型参数,来覆盖默认的推倒行为
const teacherId = ref<String | Number>('4')
通过 定义一个接口 来做类型约束
interface StudentInfoFormat {
name: string,
id: number,
age: number,
hobby ?: string
}
const info : StudentInfoFormat = reactive({name:'海军',id:2})
const getStudentInfo = (data : StudentInfoFormat) => {
console.log(data)
}
getStudentInfo(info)
这样就有很好的类型提示。
上面定义来 一个 可选属性 hobby,在初始化时,没有传递 age属性, 下面提示了 缺少 age 。
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('')
若返回的参数不是指定的参数类型则会报错
computed<T>(() => {})
const num = computed<number>(() => {
return 99 * 44
})
在处理原生 DOM 事件时,应该为我们传递给事件处理函数的参数正确地标注类型。
<input type="text" @change="change">
const change = (e : Event) => {
console.log((e.target as HTMLInputElement).value)
}
当 参数 e 没有标注类型时, 它的类型为 any 。 如果在tsconfig.json 中配置了 "strict": true 或 "noImplicitAny": true 时报出一个 TS 错误。
我们可以显式强制转化 event 属性 , 让浏览器更好的知道类型。
在组件传值时,有时组件嵌套太深时,组件通信就变的麻烦起来了。 如果子子孙组件需要获取顶级组件状态时,那么它可以 通过 Vuex / Pinia / 事件总线 来通信,不过,不建议这样做。一般用 Vuex / Pinia 一般存储一些全局的状态时使用,这里用就小题大做了。 我们可以通过在顶级组件 Provide 提供需要的值,然后在它所嵌套的组件中注入需要的值即可,这样状态也好管理。
在Vue3 中,如果我们要给 提供的值 标注类型,可以借助这个 接口 来实 InjectionKey 接口,它是一个继承自 Symbol 的泛型类型,可以用来在提供者和消费者之间同步注入值的类型。
单独封装key 的好处,这样我们在别的组件也可以使用该key 做为类型标注。
//provideKey.ts
import { provide, inject } from 'vue'
import type { InjectionKey } from 'vue'
export const key = Symbol() as InjectionKey<string>
在顶级组件中提供 key , 供下级组件使用key
import {key} from "../../common/provideKey"
import {provide} from "vue"
provide(key,'标注类型')
如果没有key 的value 类型不是指定的类型,则会报警告提示
下级组件注入 key
import { inject } from "vue"
const key = inject('token')
有时,我们需要通过原生DOM 做一些操作,就需要获取到原生DOM.
下面是 获取表单元素,进入该组件时,自动聚焦。
<el-input v-model="str" placeholder="" size="normal" clearable @change="" ref="formInputRef"></el-input>
const str = ref("测试")
const formInputRef = ref<HTMLInputElement | null>(null)
onMounted(() => {
formInputRef.value?.focus()
})
模板引用需要通过一个显式指定的泛型参数和一个初始值 null 来创建
有时候,我们需要直接操作子组件来获取它的状态和方法。 在Vue2.x 中,我们可以直接在子组件中绑定ref,然后通过 this.$refs.绑定的ref 就可以使用了。
在 Vue 3中,我们也是如此。但是在组合式API 中,调用的时候,不用this了,通过 ref.value 来操作。
想要给给子组件标注类型时:
我们就需要先通过 typeof 来 获取组件的类型,然后通过TypeScript 内置的InstanceType 工具类型来获取其实例类型,就可以操作子组件了。
<ts-component ref="tsRef" ></ts-component>
<script setup lang="ts">
import TsComponent from '../TsComponent/index.vue'
const tsRef = ref(null)
tsRef.value?.alerTest('测试') //调用子组件方法
</script>
<script setup lang="ts">
import TsComponent from '../TsComponent/index.vue'
const tsRef = ref<InstanceType<typeof TsComponent> | null>(null)
tsRef.value?.alerTest('测试') //调用子组件方法
</script>
在Vue3 中 选项式 API 想要做类型推倒,得使用 defineComponent() 来包装组件。
需要使用 defineComponent() 包装组件包裹起来,
只做简单类型推倒
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
props:{
code:{
type:Number,
default:22,
required:true,
},
content:{
type:String,
default: () => "测试"
}
},
})
</script>
有时,我们需要对Props 的属性要求是复杂类型或者多层级进行类型要求,按一般的写法是实现不了的,可以通过 PropType 这个工具类型来标记更复杂的 props 类型
import { defineComponent } from 'vue'
import type { PropType } from 'vue'
interface InfoFormat {
code: {
type: number,
required: true
}
msg:{
type: string
}
}
export default defineComponent({
props:{
info: {
type: Object as PropType<InfoFormat>,
}
},
})
如果你的 TypeScript 版本低于 4.7,在使用函数作为 prop 的 validator 和 default 选项值时需要格外小心——确保使用箭头函数
可以给 emits 选项提供一个对象来声明组件所触发的事件,以及这些事件所期望的参数类型。试图触发未声明的事件会抛出一个类型错误
emits:{
getData(ctx:{name:string,age:number}) {
// 内部可以加 一些验证
// return
return
}
},
//触发事件
this.$emit('getData',{name:'海军',age:22})
如果我们给emit 事件加了参数类型验证,当触发事件时,没有传递参数或者参数类型错误 都会警告提示。
计算属性会自动根据其返回值来推导其类型。
在某些场景,我们需要显示的标记出 计算属性的类型。因为在某些 TypeScript 因循环引用而无法推导类型的情况下,可能必须进行显式的类型标注。
computed:{
filterData(): Array<{code:number,msg:string}> {
if(this.arrList.length == 0) {
return []
} else{
return this.arrList.filter(i => i.code == 200)
}
}
},
import { defineComponent } from 'vue'
export default defineComponent({
methods: {
handleChange(event: Event) {
console.log((event.target as HTMLInputElement).value)
}
}
})
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。