imtoken钱包官网下载 to|definecomponent

作者: imtoken钱包官网下载 to
2024-03-09 21:40:50

vue3中defineComponent 的作用详解 - 掘金

vue3中defineComponent 的作用详解 - 掘金

首页 首页

沸点

课程

直播

活动

竞赛

商城

APP

插件 搜索历史

清空

创作者中心

写文章 发沸点 写笔记 写代码 草稿箱 创作灵感

查看更多

会员

登录

注册

vue3中defineComponent 的作用详解

前端_小姜

2023-02-10

7,983

这篇文章主要介绍了vue3中defineComponent 的作用,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

vue3中,新增了 defineComponent ,它并没有实现任何的逻辑,只是把接收的 Object 直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务 TypeScript 而存在的。

我都知道普通的组件就是一个普通的对象,既然是一个普通的对象,那自然就不会获得自动的提示,

import { defineComponent } from 'vue'

const component = {

name: 'Home',

props:{

data: String,

},

setup // 没有该有的提示,这非常的不友好

}

export default component

但是当我们加上 defineComponent() 之后,就完全不一样了,可以获得自动提示,vue2、vue3的自动提示都有

import { defineComponent } from 'vue'

const component = {

name: 'Home',

props:{

data: String,

},

setup(){

// setup 可接受两个参数,一个props,和 context

}

}

export default component

接下来看看 setup 中的两个参数 props 与 context ,

props指组件传递来的参数,并且ts可以推论出props的类型.props也就是 vue2 中组件中的 props

context 有三个属性 attrs slots emit 分别对应vue2中的attrs属性、slots插槽、$emit发送事件

import { defineComponent } from 'vue'

const component = {

name: 'Home',

props:{

data: String,

},

setup(props, context){

// props.data

// context.attrs context.slots context.emit

}

}

export default component

扩展知识:

vue3之组件结构(defineComponent,setup函数)

在vue3中,对组件整体结构做一些调整,先看一个组件案例:

import {ref, reactive, defineComponent, Ref, onMounted} from "vue";

import {settingsStore} from "/@/store/module/settings";

import {IRoleList} from "/@/interface/role/list.interface";

import {IHttpResult} from "/@/interface/common.interface";

import { ILogListParams } from "/@/interface/settings/log.interface";

export default defineComponent({

name: "LogList",

setup() {

const logList: Ref = ref([]);

const columns = [

...

];

const pagination = ref({

"show-quick-jumper": true,

total: 100,

current: 1,

"show-size-changer": true,

"show-total": (total: number, range: number[]) => `${range[0]}-${range[1]} 共 ${total} 条`,

"pageSize": 10

});

const columnsList = ref(columns);

const params: ILogListParams = reactive({

page: 1,

pageSize: 10

});

onMounted(() => {

findLogList();

});

/*查询日志列表*/

const findLogList = () => {

settingsStore.findLogList(params).then((res: IHttpResult) => {

const data = res.data;

pagination.value.total = data.total;

logList.value = data.list;

});

};

/*修改状态*/

const onChange = (pagination: {current: number, pageSize: number}) => {

params.page = pagination.current;

params.pageSize = pagination.pageSize;

};

/*删除*/

const onDelete = (id: number) => {

alert(id);

};

return {

columnsList,

logList,

onDelete,

onChange,

pagination

};

}

});

从上面组件代码中,可以看出在vue3中没有this对象, 所有的逻辑代码都写在setup方法里面.

若是要在HTML模板页面中使用变量或者方法, 需要在setup方法return出去.

setup是Vue3 的一大特性函数, 它有几个特性:

1、setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数

2、setup函数是 Composition API(组合API)的入口

3、在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用

setup函数的注意点:

vue3虽然支持vue2.x版本的写法,但也有一些要注意的地方

1、由于在执行 setup函数的时候,还没有执行 Created 生命周期方法,所以在 setup 函数中,无法使用 data 和 methods 的变量和方法

2、由于我们不能在 setup函数中使用 data 和 methods,所以 Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined

3、setup函数只能是同步的不能是异步的

上面的组件中用defineComponent包裹了组件;

defineComponent函数,只是对setup函数进行封装,返回options的对象;

defineComponent最重要的是:在TypeScript下,给予了组件 正确的参数类型推断 。

defineComponent可以给组件的setup方法准确的参数类型定义.

defineComponent 可以接受显式的自定义 props 接口或从属性验证对象中自动推断

defineComponent 可以正确适配无 props、数组 props 等形式

引入 defineComponent() 以正确推断 setup() 组件的参数类型

延申:

文档中第一句话很容易看懂,说白了就是从实现上看,用了或者跟不用这个api没多大区别但是呢,第二句话说的好像又有一些区别,不过我没太看懂。。。于是我自己的第一个问题就来了:

问题一:defineComponent 这个API用起来到底和不用有什么区别???

1. 显示 Vue Options 提示。

这个API一般是在ts或者tsx文件中使用的,所以,当我们创建了一个这种类型的文件后,它是不知道我们是要去写 vue 实例代码的,所以在这种情况下,我们需要defineComponent来帮我们做内部的一些options的提示,我们可以看一个使用了defineComponent和没有使用defineComponent的例子:

当然这背后的原理是利用 TypeScript 定义了defineComponent 参数类型实现的。

2. 给予正确的参数类型推断。

拿 setup 来说,defineComponent 可以为 setup 函数的 props 传参做出正确的类型推断,看下图:

如果没有使用 defineComponent 的话,是没有办法推断出来的,需要自己显式地去定义类型。

3. 可返回一个合成类型的构造函数。

这也是官方文档中所说的,我在代码中尝试了一下,发现确实可以在其返回的构造函数中去定义一些钩子和属性等,如下图:

这就是目前我对这个API的一些理解吧~后续有新发现再补充

到此这篇关于vue3中defineComponent 的作用的文章就介绍到这了,

更新补充:

前端_小姜

前端小菜鸡

50

文章

31k

阅读

6

粉丝 目录 收起

延申:

问题一:defineComponent 这个API用起来到底和不用有什么区别???

1. 显示 Vue Options 提示。

2. 给予正确的参数类型推断。

3. 可返回一个合成类型的构造函数。

defineComponent | Vue3

defineComponent | Vue3

Vue3

Github

Github

阅前必读 开篇词 | 为什么要学习源码前置知识 ProxySet、Map、WeakSet、WeakMapCompositionTypeScriptSpec语法全局概览 目录结构createAppdefineComponenthnextTickExample 基本范例响应式系统 整体概览reactivereactive.specrefref.specbaseHandlerseffecteffect.speccomputedcomputed.spec编绎模块 compilerparse.speccompile.specRuntime runtime # defineComponent 实现方式的 defineComponent 只是返回传递给它的对象。但是,在类型方面,返回的值具有一个合成类型的构造函数,用于手动渲染函数、 TSX 和 IDE 工具支持 # 从一个例子开始 import { defineComponent } from 'vue'

const MyComponent = defineComponent({

data() {

return { count: 1 }

},

methods: {

increment() {

this.count++

}

}

})

console.log(`MyComponent:${MyComponent}`)

1234567891011121314亲自试一试

createApp

h

MIT Licensed | Copyright @ 2020-2021 Vue3js.cn 京ICP备15001338号-6

Vue 中的 defineComponent - 掘金

Vue 中的 defineComponent - 掘金

首页 首页

沸点

课程

直播

活动

竞赛

商城

APP

插件 搜索历史

清空

创作者中心

写文章 发沸点 写笔记 写代码 草稿箱 创作灵感

查看更多

会员

登录

注册

Vue 中的 defineComponent

滴滴前端技术团队

2021-08-10

27,519

关注

@滴滴出行

作者:崔静

defineComponent 本身的功能很简单,但是最主要的功能是为了 ts 下的类型推到。对于一个 ts 文件,如果我们直接写

export default {}

这个时候,对于编辑器而言,{} 只是一个 Object 的类型,无法有针对性的提示我们对于 vue 组件来说 {} 里应该有哪些属性。但是增加一层 defineComponet 的话,

export default defineComponent({})

这时,{} 就变成了 defineComponent 的参数,那么对参数类型的提示,就可以实现对 {} 中属性的提示,外还可以进行对参数的一些类型推导等操作。

但是上面的例子,如果你在 vscode 的用 .vue 文件中尝试的话,会发现不写 defineComponent 也一样有提示。这个其实是 Vetur 插件进行了处理。

下面看 defineComponent 的实现,有4个重载,先看最简单的第一个,这里先不关心 DefineComponent 是什么,后面细看。

// overload 1: direct setup function

// (uses user defined props interface)

export function defineComponent(

setup: (

props: Readonly,

ctx: SetupContext

) => RawBindings | RenderFunction

): DefineComponent

defineComponet 参数为 function, function 有两个参数 props 和 ctx,返回值类型为 RawBindings 或者 RenderFunction。defineComponet 的返回值类型为 DefineComponent。这其中有两个泛型 Props 和 RawBindings。Props 会根据我们实际写的时候给 setup 第一个参数传入的类型而确定,RawBindings 则根据我们 setup 返回值来确定。一大段话比较绕,写一个类似的简单的例子来看:

类似 props 用法的简易 demo 如下,我们给 a 传入不同类型的参数,define 返回值的类型也不同。这种叫 Generic Functions

declare function define(a: Props): Props

const arg1:string = '123'

const result1 = define(arg1) // result1:string

const arg2:number = 1

const result2 = define(arg2) // result2: number

类似 RawBindings 的简易 demo如下: setup 返回值类型不同,define 返回值的类型也不同

declare function define(setup: ()=>T): T

const arg1:string = '123'

const resul1 = define(() => {

return arg1

})

const arg2:number = 1

const result2 = define(() => {

return arg2

})

由上面两个简易的 demo,可以理解重载1的意思,defineComponet 返回类型为DefineComponent,其中 Props 为 setup 第一个参数类型;RawBindings 为 setup 返回值类型,如果我们返回值为函数的时候,取默认值 object。从中可以掌握一个 ts 推导的基本用法,对于下面的定义

declare function define(a: T): T

可以根据运行时传入的参数,来动态决定 T 的类型 这种方式也是运行时类型和 typescript 静态类型的唯一联系,很多我们想通过运行时传入参数类型,来决定其他相关类型的时候,就可以使用这种方式。

接着看 definComponent,它的重载2,3,4分别是为了处理 options 中 props 的不同类型。看最常见的 object 类型的 props 的声明

export function defineComponent<

// the Readonly constraint allows TS to treat the type of { required: true }

// as constant instead of boolean.

PropsOptions extends Readonly,

RawBindings,

D,

C extends ComputedOptions = {},

M extends MethodOptions = {},

Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,

Extends extends ComponentOptionsMixin = ComponentOptionsMixin,

E extends EmitsOptions = Record,

EE extends string = string

>(

options: ComponentOptionsWithObjectProps<

PropsOptions,

RawBindings,

D,

C,

M,

Mixin,

Extends,

E,

EE

>

): DefineComponent

和上面重载1差不多的思想,核心思想也是根据运行时写的 options 中的内容推导出各种泛型。在 vue3 中 setup 的第一个参数是 props,这个 props 的类型需要和我们在 options 传入的一致。这个就是在ComponentOptionsWithObjectProps中实现的。代码如下

export type ComponentOptionsWithObjectProps<

PropsOptions = ComponentObjectPropsOptions,

RawBindings = {},

D = {},

C extends ComputedOptions = {},

M extends MethodOptions = {},

Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,

Extends extends ComponentOptionsMixin = ComponentOptionsMixin,

E extends EmitsOptions = EmitsOptions,

EE extends string = string,

Props = Readonly>,

Defaults = ExtractDefaultPropTypes

> = ComponentOptionsBase<

Props,

RawBindings,

D,

C,

M,

Mixin,

Extends,

E,

EE,

Defaults

> & {

props: PropsOptions & ThisType

} & ThisType<

CreateComponentPublicInstance<

Props,

RawBindings,

D,

C,

M,

Mixin,

Extends,

E,

Props,

Defaults,

false

>

>

export interface ComponentOptionsBase<

Props,

RawBindings,

D,

C extends ComputedOptions,

M extends MethodOptions,

Mixin extends ComponentOptionsMixin,

Extends extends ComponentOptionsMixin,

E extends EmitsOptions,

EE extends string = string,

Defaults = {}

>

extends LegacyOptions,

ComponentInternalOptions,

ComponentCustomOptions {

setup?: (

this: void,

props: Props,

ctx: SetupContext

) => Promise | RawBindings | RenderFunction | void

//...

}

很长一段,同样的先用一个简化版的 demo 来理解一下:

type TypeA = {

a: T1,

b: T2,

c: T3

}

declare function define(options: TypeA): T1

const result = define({

a: '1',

b: 1,

c: {}

}) // result: string

根据传入的 options 参数 ts 会推断出 T1,T2,T3的类型。得到 T1, T2, T3 之后,可以利用他们进行其他的推断。稍微改动一下上面的 demo,假设 c 是一个函数,里面的参数类型由 a 的类型来决定:

type TypeA = TypeB

type TypeB = {

a: T1

b: T2,

c: (arg:T1)=>{}

}

const result = define({

a: '1',

b: 1,

c: (arg) => { // arg 这里就被会推导为一个 string 的类型

return arg

}

})

然后来看 vue 中的代码,首先 defineComponent 可以推导出 PropsOptions。但是 props 如果是对象类型的话,写法如下

props: {

name: {

type: String,

//... 其他的属性

}

}

而 setup 中的 props 参数,则需要从中提取出 type 这个类型。所以在 ComponentOptionsWithObjectProps 中

export type ComponentOptionsWithObjectProps<

PropsOptions = ComponentObjectPropsOptions,

//...

Props = Readonly>,

//...

>

通过 ExtracPropTypes 对 PropsOptions 进行转化,然后得到 Props,再传入 ComponentOptionsBase,在这个里面,作为 setup 参数的类型

export interface ComponentOptionsBase<

Props,

//...

>

extends LegacyOptions,

ComponentInternalOptions,

ComponentCustomOptions {

setup?: (

this: void,

props: Props,

ctx: SetupContext

) => Promise | RawBindings | RenderFunction | void

这样就实现了对 props 的推导。

this 的作用

在 setup 定义中第一个是 this:void 。我们在 setup 函数中写逻辑的时候,会发现如果使用了 this.xxx IDE 中会有错误提示

Property 'xxx' does not exist on type 'void'

这里通过设置 this:void来避免我们在 setup 中使用 this。

this 在 js 中是一个比较特殊的存在,它是根据运行上上下文决定的,所以 typescript 中有时候无法准确的推导出我们代码中使用的 this 是什么类型的,所以 this 就变成了 any,各种类型提示/推导啥的,也都无法使用了(注意:只有开启了 noImplicitThis 配置, ts 才会对 this 的类型进行推导)。为了解决这个问题,typescript 中 function 的可以明确的写一个 this 参数,例如官网的例子:

interface Card {

suit: string;

card: number;

}

interface Deck {

suits: string[];

cards: number[];

createCardPicker(this: Deck): () => Card;

}

let deck: Deck = {

suits: ["hearts", "spades", "clubs", "diamonds"],

cards: Array(52),

// NOTE: The function now explicitly specifies that its callee must be of type Deck

createCardPicker: function (this: Deck) {

return () => {

let pickedCard = Math.floor(Math.random() * 52);

let pickedSuit = Math.floor(pickedCard / 13);

return { suit: this.suits[pickedSuit], card: pickedCard % 13 };

};

},

};

let cardPicker = deck.createCardPicker();

let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

明确的定义出在 createCardPicker 中的 this 是 Deck 的类型。这样在 createCardPicker 中 this 下可使用的属性/方法,就被限定为 Deck 中的。

另外和 this 有关的,还有一个 ThisType。

ExtractPropTypes 和 ExtractDefaultPropTypes

上面提到了,我们写的 props

{

props: {

name1: {

type: String,

require: true

},

name2: {

type: Number

}

}

}

经过 defineComponent 的推导之后,会被转换为 ts 的类型

ReadOnly<{

name1: string,

name2?: number | undefined

}>

这个过程就是利用 ExtractPropTypes 实现的。

export type ExtractPropTypes = O extends object

? { [K in RequiredKeys]: InferPropType } &

{ [K in OptionalKeys]?: InferPropType }

: { [K in string]: any }

根据类型中清晰的命名,很好理解:利用 RrequiredKeys 和 OptionsKeys 将 O 按照是否有 required 进行拆分(以前面props为例子)

{

name1

} & {

name2?

}

然后每一组里,用 InferPropType 推导出类型。

InferPropType

在理解这个之前,先理解一些简单的推导。首先我们在代码中写

props = {

type: String

}

的话,经过 ts 的推导,props.type 的类型是 StringConstructor。所以第一步需要从 StringConstructor/ NumberConstructor 等 xxConstrucror 中得到对应的类型 string/number 等。可以通过 infer 来实现

type a = StringConstructor

type ConstructorToType = T extends { (): infer V } ? V : never

type c = ConstructorToType // type c = String

上面我们通过 ():infer V 来获取到类型。之所以可以这样用,和 String/Number 等类型的实现有关。javascript 中可以写

const key = String('a')

此时,key 是一个 string 的类型。还可以看一下 StringConstructor 接口类型表示

interface StringConstructor {

new(value?: any): String;

(value?: any): string;

readonly prototype: String;

fromCharCode(...codes: number[]): string;

}

上面有一个 ():string ,所以通过 extends {(): infer V} 推断出来的 V 就是 string。

然后再进一步,将上面的 a 修改成 propsOptions 中的内容,然后把 ConstructorToType 中的 infer V 提到外面一层来判断

type a = StringConstructor

type ConstructorType = { (): T }

type b = a extends {

type: ConstructorType

required?: boolean

} ? V : never // type b = String

这样就简单实现了将 props 中的内容转化为 type 中的类型。

因为 props 的 type 支持很多中写法,vue3 中实际的代码实现要比较复杂

type InferPropType = T extends null

? any // null & true would fail to infer

: T extends { type: null | true }

? any

// As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean`

// 这里单独判断了 ObjectConstructor 和 BooleanConstructor

: T extends ObjectConstructor | { type: ObjectConstructor }

? Record

: T extends BooleanConstructor | { type: BooleanConstructor }

? boolean

: T extends Prop ? (unknown extends V ? D : V) : T

// 支持 PropOptions 和 PropType 两种形式

type Prop = PropOptions | PropType

interface PropOptions {

type?: PropType | true | null

required?: boolean

default?: D | DefaultFactory | null | undefined | object

validator?(value: unknown): boolean

}

export type PropType = PropConstructor | PropConstructor[]

type PropConstructor =

| { new (...args: any[]): T & object } // 可以匹配到其他的 Constructor

| { (): T } // 可以匹配到 StringConstructor/NumberConstructor 和 () => string 等

| PropMethod // 匹配到 type: (a: number, b: string) => string 等 Function 的形式

// 对于 Function 的形式,通过 PropMethod 构造成了一个和 stringConstructor 类型的类型

// PropMethod 作为 PropType 类型之一

// 我们写 type: Function as PropType<(a: string) => {b: string}> 的时候,就会被转化为

// type: (new (...args: any[]) => ((a: number, b: string) => {

// a: boolean;

// }) & object) | (() => (a: number, b: string) => {

// a: boolean;

// }) | {

// (): (a: number, b: string) => {

// a: boolean;

// };

// new (): any;

// readonly prototype: any;

// }

// 然后在 InferPropType 中就可以推断出 (a:number,b:string)=> {a: boolean}

type PropMethod =

T extends (...args: any) => any // if is function with args

? {

new (): TConstructor;

(): T;

readonly prototype: TConstructor

} // Create Function like constructor

: never

RequiredKeys

这个用来从 props 中分离出一定会有值的 key,源码如下

type RequiredKeys = {

[K in keyof T]: T[K] extends

| { required: true }

| { default: any }

// don't mark Boolean props as undefined

| BooleanConstructor

| { type: BooleanConstructor }

? K

: never

}[keyof T]

除了明确定义 reqruied 以外,还包含有 default 值,或者 boolean 类型。因为对于 boolean 来说如果我们不传入,就默认为 false;而有 default 值的 prop,一定不会是 undefined

OptionalKeys

有了 RequiredKeys, OptionsKeys 就很简单了:排除了 RequiredKeys 即可

type OptionalKeys = Exclude>

ExtractDefaultPropTypes 和 ExtractPropTypes 类似,就不写了。

推导 options 中的 method,computed, data 返回值, 都和上面推导 props 类似。

emits options

vue3 的 options 中新增加了一个 emits 配置,可以显示的配置我们在组件中要派发的事件。配置在 emits 中的事件,在我们写 $emit 的时候,会作为函数的第一个参数进行提示。

对获取 emits 中配置值的方式和上面获取 props 中的类型是类似的。$emit的提示,则是通过 ThisType 来实现的(关于 ThisType 参考另外一篇文章介绍)。下面是简化的 demo

declare function define(props:{

emits: T,

method?: {[key: string]: (...arg: any) => any}

} & ThisType<{

$emits: (arg: T) => void

}>):T

const result = define({

emits: {

key: '123'

},

method: {

fn() {

this.$emits(/*这里会提示:arg: {

key: string;

}*/)

}

}

})

上面会推导出 T 为 emits 中的类型。然后 & ThisType ,使得在 method 中就可以使用 this.$emit。再将 T 作为 $emit 的参数类型,就可以在写 this.$emit的时候进行提示了。

然后看 vue3 中的实现

export function defineComponent<

//... 省却其他的

E extends EmitsOptions = Record,

//...

>(

options: ComponentOptionsWithObjectProps<

//...

E,

//...

>

): DefineComponent

export type ComponentOptionsWithObjectProps<

//..

E extends EmitsOptions = EmitsOptions,

//...

> = ComponentOptionsBase< // 定义一个 E 的泛型

//...

E,

//...

> & {

props: PropsOptions & ThisType

} & ThisType<

CreateComponentPublicInstance< // 利用 ThisType 实现 $emit 中的提示

//...

E,

//...

>

>

// ComponentOptionsWithObjectProps 中 包含了 ComponentOptionsBase

export interface ComponentOptionsBase<

//...

E extends EmitsOptions, // type EmitsOptions = Record any) | null> | string[]

EE extends string = string,

Defaults = {}

>

extends LegacyOptions,

ComponentInternalOptions,

ComponentCustomOptions {

//..

emits?: (E | EE[]) & ThisType // 推断出 E 的类型

}

export type ComponentPublicInstance<

//...

E extends EmitsOptions = {},

//...

> = {

//...

$emit: EmitFn // EmitFn 来提取出 E 中的 key

//...

}

在一边学习一边实践的时候踩到一个坑。踩坑过程:将 emits 的推导过程实现了一下

export type ObjectEmitsOptions = Record<

string,

((...args: any[]) => any) | null

>

export type EmitsOptions = ObjectEmitsOptions | string[];

declare function define, EE extends string = string>(options: E| EE[]): (E | EE[]) & ThisType

然后用下面的方式来验证结果

const emit = ['key1', 'key2']

const a = define(emit)

看 ts 提示的时候发现,a 的类型是 const b: string[] & ThisType,但是实际中 vue3 中写同样数组的话,提示是 const a: (("key1" | "key2")[] & ThisType) | (("key1" | "key2")[] & ThisType)

纠结好久,最终发现写法的不同:用下面写法的话推导出来结果一致

define(['key1', 'key2'])

但是用之前的写法,通过变量传入的时候,ts 在拿到 emit 时候,就已经将其类型推导成了 string[],所以 define 函数中拿到的类型就变成了 string[],而不是原始的 ['key1', 'key2']

因此需要注意:在 vue3 中定义 emits 的时候,建议直接写在 emits 中写,不要提取为单独的变量再传给 emits

真的要放在单独变量里的话,需要进行处理,使得 '[key1', 'key2'] 的变量定义返回类型为 ['key1', 'key2'] 而非 string[]。可以使用下面两种方式:

方式一

const keys = ["key1", "key2"] as const; // const keys: readonly ["key1", "key2"]

这种方式写起来比较简单。但是有一个弊端,keys 为转为 readonly 了,后期无法对 keys 进行修改。

参考文章2 ways to create a Union from an Array in Typescript

方式二

type UnionToIntersection = (T extends any ? (v: T) => void : never) extends (v: infer V) => void ? V : never

type LastOf = UnionToIntersection T : never> extends () => infer R ? R : never

type Push = [ ...T, V]

type UnionToTuple, N = [T] extends [never] ? true : false> = N extends true ? [] : Push>, L>

declare function tuple(arr: T[]): UnionToTuple

const c = tuple(['key1', 'key2']) // const c: ["key1", "key2"]

首先通过 arr: T[] 将 ['key1', 'key2'] 转为 union,然后通过递归的方式, LastOf 获取 union 中的最后一个,Push到数组中。

mixins 和 extends

vue3 中写在 mixins 或 extends 中的内容可以在 this 中进行提示。对于 mixins 和 extends 来说,与上面其他类型的推断有一个很大的区别:递归。所以在进行类型判断的时候,也需要进行递归处理。举个简单的例子,如下

const AComp = {

methods: {

someA(){}

}

}

const BComp = {

mixins: [AComp],

methods: {

someB() {}

}

}

const CComp = {

mixins: [BComp],

methods: {

someC() {}

}

}

对于 CComp 中的 this 的提示,应该有方法 someB 和 someA。为了实现这个提示,在进行类型推断的时候,需要一个类似下面的 ThisType

ThisType<{

someA

} & {

someB

} & {

someC

}>

所以对于 mixins 的处理,就需要递归获取 component 中的 mixins 中的内容,然后将嵌套的类型转化为扁平化的,通过 & 来链接。看源码中实现:

// 判断 T 中是否有 mixin

// 如果 T 含有 mixin 那么这里结果为 false,以为 {mixin: any} {mixin?: any} 是无法互相 extends 的

type IsDefaultMixinComponent = T extends ComponentOptionsMixin

? ComponentOptionsMixin extends T ? true : false

: false

//

type IntersectionMixin = IsDefaultMixinComponent extends true

? OptionTypesType<{}, {}, {}, {}, {}> // T 不包含 mixin,那么递归结束,返回 {}

: UnionToIntersection> // 获取 T 中 Mixin 的内容进行递归

// ExtractMixin(map type) is used to resolve circularly references

type ExtractMixin = {

Mixin: MixinToOptionTypes

}[T extends ComponentOptionsMixin ? 'Mixin' : never]

// 通过 infer 获取到 T 中 Mixin, 然后递归调用 IntersectionMixin

type MixinToOptionTypes = T extends ComponentOptionsBase<

infer P,

infer B,

infer D,

infer C,

infer M,

infer Mixin,

infer Extends,

any,

any,

infer Defaults

>

? OptionTypesType

&

IntersectionMixin &

IntersectionMixin

: never

extends 和 mixin 的过程相同。然后看 ThisType 中的处理

ThisType<

CreateComponentPublicInstance<

Props,

RawBindings,

D,

C,

M,

Mixin,

Extends,

E,

Props,

Defaults,

false

>

>

export type CreateComponentPublicInstance<

P = {},

B = {},

D = {},

C extends ComputedOptions = {},

M extends MethodOptions = {},

Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,

Extends extends ComponentOptionsMixin = ComponentOptionsMixin,

E extends EmitsOptions = {},

PublicProps = P,

Defaults = {},

MakeDefaultsOptional extends boolean = false,

// 将嵌套的结构转为扁平化的

PublicMixin = IntersectionMixin & IntersectionMixin,

// 提取 props

PublicP = UnwrapMixinsType & EnsureNonVoid

,

// 提取 RawBindings,也就是 setup 返回的内容

PublicB = UnwrapMixinsType & EnsureNonVoid,

// 提取 data 返回的内容

PublicD = UnwrapMixinsType & EnsureNonVoid,

PublicC extends ComputedOptions = UnwrapMixinsType &

EnsureNonVoid,

PublicM extends MethodOptions = UnwrapMixinsType &

EnsureNonVoid,

PublicDefaults = UnwrapMixinsType &

EnsureNonVoid

> = ComponentPublicInstance< // 上面结果传给 ComponentPublicInstance,生成 this context 中的内容

PublicP,

PublicB,

PublicD,

PublicC,

PublicM,

E,

PublicProps,

PublicDefaults,

MakeDefaultsOptional,

ComponentOptionsBase

>

以上就是整体大部分的 defineComponent 的实现,可以看出,他纯粹是为了类型推导而生的,同时,这里边用到了很多很多类型推导的技巧,还有一些这里没有涉及,感兴趣的同学可以去仔细看下 Vue 中的实现。

滴滴前端技术

@滴滴出行

63

文章

501k

阅读

6.5k

粉丝 目录 收起

ExtractPropTypes 和 ExtractDefaultPropTypes

emits options

mixins 和 extends

友情链接:

风流小相师笔趣阁

超级傻婿奶爸

闯关东:我!朱传武誓做铁血军神

亿万富翁意外重生1988

主角叫夜帝,都市小说

react父组件触发子组件方法

vue3中的defineComponent详解 - 掘金

vue3中的defineComponent详解 - 掘金

首页 首页

沸点

课程

直播

活动

竞赛

商城

APP

插件 搜索历史

清空

创作者中心

写文章 发沸点 写笔记 写代码 草稿箱 创作灵感

查看更多

会员

登录

注册

vue3中的defineComponent详解

前端_小姜

2023-03-30

400

vue3中,新增了 defineComponent ,它并没有实现任何的逻辑,只是把接收的 Object 直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务 TypeScript 而存在的。最主要的功能是为了 ts 下的类型推断

下面是我想通过了解一段项目代码的意义来理解defineComponent

下面是我在vue3项目中看到的真实情况:

1、没有defineComponent时候:

1、有defineComponent时候:

以上就是我刚接触vue3和ts没多久时在项目中实际遇到的问题,但是搞半天我还是没有弄明白我疑惑的地方,下面是使用GPT3.5 Model了解的结果,多少有点清楚了:

import { defineComponent } from 'vue';

export default defineComponent({

name: 'MyComponent',

props: {

message: {

type: String,

required: true

},

count: {

type: Number,

default: 0

}

},

data() {

return {

foo: 'bar',

baz: 123

};

},

methods: {

handleClick() {

this.baz++;

}

},

computed: {

computedMessage() {

return this.message.toUpperCase();

}

},

template: `

`

});

在这个例子中,我们使用defineComponent函数来定义一个名为MyComponent的组件。在props属性中,我们明确指定了message和count属性的类型,并且将message属性标记为必需的。在data方法中,我们返回了一个对象,明确指定了foo和baz属性的类型。在methods方法中,我们定义了一个handleClick方法。在computed属性中,我们定义了一个computedMessage计算属性。最后,在template中,我们使用了这些属性和方法来渲染组件的模板。

前端_小姜

前端小菜鸡

50

文章

32k

阅读

6

粉丝 目录 收起

下面是我在vue3项目中看到的真实情况:

1、没有defineComponent时候:

1、有defineComponent时候:

全局 API:常规 | Vue.js

全局 API:常规 | Vue.js

直接跳到内容Vue.js搜索主导航文档 深度指南互动教程示例快速上手术语表错误码参照表Vue 2 文档从 Vue 2 迁移API演练场生态系统 资源合作伙伴主题UI 组件证书找工作T-Shirt 商店官方库Vue RouterPinia工具链指南视频课程Vue MasteryVue School帮助Discord 聊天室GitHub 论坛DEV Community动态博客Twitter活动新闻简报关于 常见问题团队版本发布社区指南行为规范纪录片赞助合作伙伴English 日本語 Українська Français 한국어 Português বাংলা Italiano 帮助我们翻译!githubtwitterdiscord外观githubtwitterdiscord菜单本页目录 侧边栏导航全局 API应用实例通用组合式 APIsetup()响应式: 核心响应式: 工具响应式: 进阶生命周期钩子依赖注入选项式 API状态选项渲染选项生命周期选项组合选项其他杂项组件实例内置内容指令组件特殊元素特殊 Attributes单文件组件语法定义

vue

参考 this.$nextTick()defineComponent() ​在定义 Vue 组件时提供类型推导的辅助函数。类型ts// 选项语法

function defineComponent(

component: ComponentOptions

): ComponentConstructor

// 函数语法 (需要 3.3+)

function defineComponent(

setup: ComponentOptions['setup'],

extraOptions?: ComponentOptions

): () => any为了便于阅读,对类型进行了简化。详细信息第一个参数是一个组件选项对象。返回值将是该选项对象本身,因为该函数实际上在运行时没有任何操作,仅用于提供类型推导。注意返回值的类型有一点特别:它会是一个构造函数类型,它的实例类型是根据选项推断出的组件实例类型。这是为了能让该返回值在 TSX 中用作标签时提供类型推导支持。你可以像这样从 defineComponent() 的返回类型中提取出一个组件的实例类型 (与其选项中的 this 的类型等价):tsconst Foo = defineComponent(/* ... */)

type FooInstance = InstanceType函数签名 ​defineComponent() 还有一种备用签名,旨在与组合式 API 和 渲染函数或 JSX 一起使用。与传递选项对象不同的是,它需要传入一个函数。这个函数的工作方式与组合式 API 的 setup() 函数相同:它接收 props 和 setup 上下文。返回值应该是一个渲染函数——支持 h() 和 JSX:jsimport { ref, h } from 'vue'

const Comp = defineComponent(

(props) => {

// 就像在

复制VI. 全文总结引入 defineComponent() 以正确推断 setup() 组件的参数类型defineComponent 可以正确适配无 props、数组 props 等形式defineComponent 可以接受显式的自定义 props 接口或从属性验证对象中自动推断在 tsx 中,element-ui 等全局注册的组件依然要用 kebab-case 形式在 tsx 中,v-model 要用 model={{ value, callback }} 写法在 tsx 中,scoped slots 要用 scopedSlots={{ foo: (scope) => () }} 写法defineComponent 并不适用于函数式组件,应使用 RenderContext 解决--End--本文参与 腾讯云自媒体分享计划,分享自微信公众号。原始发表:2020-10-21,如有侵权请联系 cloudcommunity@tencent.com 删除vue.jsapi编程算法https单元测试本文分享自 云前端 微信公众号,前往查看如有侵权,请联系 cloudcommunity@tencent.com 删除。本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!vue.jsapi编程算法https单元测试评论登录后参与评论0 条评论热度最新登录 后参与评论推荐阅读LV.关注文章0获赞0目录I. 测试用例[test case 1] 无 props[test case 2] 数组形式的 props[test case 3] 自动推断 props[test case 4] 推断是否必须[test case 5] 显式自定义 props 接口[test case 6] 显式接口和显式类型[test case 7] 从显式类型推断 propsII. 一些基础类型定义引自 vue 2.x 中的 options 基础类型接口composition 式组件 options 类型基础接口setup 函数上下文类型接口普通键值数据计算值选项类型方法选项类型Vue 组件代理组件渲染代理属性类型定义属性验证类型定义兼容字符串和验证对象的 props 类型定义III. 官网文档中的 propsIV. defineComponent 函数签名签名 1:无 props签名 2:数组形式的 props签名3:非数组 propsV. 开发实践ParentDialog.vueVI. 全文总结领券社区专栏文章阅读清单互动问答技术沙龙技术视频团队主页腾讯云TI平台活动自媒体分享计划邀请作者入驻自荐上首页技术竞赛资源技术周刊社区标签开发者手册开发者实验室关于社区规范免责声明联系我们友情链接腾讯云开发者扫码关注腾讯云开发者领取腾讯云代金券热门产品域名注册云服务器区块链服务消息队列网络加速云数据库域名解析云存储视频直播热门推荐人脸识别腾讯会议企业云CDN加速视频通话图像分析MySQL 数据库SSL 证书语音识别更多推荐数据安全负载均衡短信文字识别云点播商标注册小程序开发网站监控数据迁移Copyright © 2013 - 2024 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有 深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569腾讯云计算(北京)有限责任公司 京ICP证150476号 |  京ICP备11018762号 | 京公网安备号11010802020287问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档Copyright © 2013 - 2024 Tencent Cloud.All Rights Reserved. 腾讯云 版权所有登录 后参与评论00

TypeScript 与选项式 API | Vue.js

TypeScript 与选项式 API | Vue.js

直接跳到内容Vue.js搜索主导航文档 深度指南互动教程示例快速上手术语表错误码参照表Vue 2 文档从 Vue 2 迁移API演练场生态系统 资源合作伙伴主题UI 组件证书找工作T-Shirt 商店官方库Vue RouterPinia工具链指南视频课程Vue MasteryVue School帮助Discord 聊天室GitHub 论坛DEV Community动态博客Twitter活动新闻简报关于 常见问题团队版本发布社区指南行为规范纪录片赞助合作伙伴English 日本語 Українська Français 한국어 Português বাংলা Italiano 帮助我们翻译!githubtwitterdiscord外观githubtwitterdiscord菜单本页目录 API 风格偏好选项式组合式?侧边栏导航开始简介快速上手基础创建一个应用模板语法响应式基础计算属性类与样式绑定条件渲染列表渲染事件处理表单输入绑定生命周期侦听器模板引用组件基础深入组件注册Props事件组件 v-model透传 Attributes插槽依赖注入异步组件逻辑复用组合式函数自定义指令插件内置组件TransitionTransitionGroupKeepAliveTeleportSuspense应用规模化单文件组件工具链路由状态管理测试服务端渲染 (SSR)最佳实践生产部署性能优化无障碍访问安全TypeScript总览TS 与组合式 APITS 与选项式 API进阶主题使用 Vue 的多种方式组合式 API 常见问答深入响应式系统渲染机制渲染函数 & JSXVue 与 Web Components动画技巧本页目录当前页面的目录为组件的 props 标注类型为组件的 emits 标注类型为计算属性标记类型为事件处理函数标注类型扩展全局属性扩展自定义选项赞助位成为赞助商TypeScript 与选项式 API ​这一章假设你已经阅读了搭配 TypeScript 使用 Vue 的概览。TIP虽然 Vue 的确支持在选项式 API 中使用 TypeScript,但在使用 TypeScript 的前提下更推荐使用组合式 API,因为它提供了更简单、高效和可靠的类型推导。为组件的 props 标注类型 ​选项式 API 中对 props 的类型推导需要用 defineComponent() 来包装组件。有了它,Vue 才可以通过 props 以及一些额外的选项,比如 required: true 和 default 来推导出 props 的类型:tsimport { 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 选项仅支持使用构造函数来作为一个 prop 的类型——没有办法指定多层级对象或函数签名之类的复杂类型。我们可以使用 PropType 这个工具类型来标记更复杂的 props 类型:tsimport { 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,

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')

}

})注意事项 ​如果你的 TypeScript 版本低于 4.7,在使用函数作为 prop 的 validator 和 default 选项值时需要格外小心——确保使用箭头函数:tsimport { defineComponent } from 'vue'

import type { PropType } from 'vue'

interface Book {

title: string

year?: number

}

export default defineComponent({

props: {

bookA: {

type: Object as PropType,

// 如果你的 TypeScript 版本低于 4.7,确保使用箭头函数

default: () => ({

title: 'Arrow Function Expression'

}),

validator: (book: Book) => !!book.title

}

}

})这会防止 TypeScript 将 this 根据函数内的环境作出不符合我们期望的类型推导。这是之前版本的一个设计限制,不过现在已经在 TypeScript 4.7 中解决了。为组件的 emits 标注类型 ​我们可以给 emits 选项提供一个对象来声明组件所触发的事件,以及这些事件所期望的参数类型。试图触发未声明的事件会抛出一个类型错误:tsimport { 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') // 类型错误

}

}

})为计算属性标记类型 ​计算属性会自动根据其返回值来推导其类型:tsimport { defineComponent } from 'vue'

export default defineComponent({

data() {

return {

message: 'Hello!'

}

},

computed: {

greeting() {

return this.message + '!'

}

},

mounted() {

this.greeting // 类型:string

}

})在某些场景中,你可能想要显式地标记出计算属性的类型以确保其实现是正确的:tsimport { 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

没有类型标注时,这个 event 参数会隐式地标注为 any 类型。这也会在 tsconfig.json 中配置了 "strict": true 或 "noImplicitAny": true 时抛出一个 TS 错误。因此,建议显式地为事件处理函数的参数标注类型。此外,在访问 event 上的属性时你可能需要使用类型断言:tsimport { 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 暴露了一个被设计为可以通过 TypeScript 模块扩展来扩展的 ComponentCustomProperties 接口:tsimport axios from 'axios'

declare module 'vue' {

interface ComponentCustomProperties {

$http: typeof axios

$translate: (key: string) => string

}

}参考:对组件类型扩展的 TypeScript 单元测试类型扩展的位置 ​我们可以将这些类型扩展放在一个 .ts 文件,或是一个影响整个项目的 *.d.ts 文件中。无论哪一种,都应确保在 tsconfig.json 中包括了此文件。对于库或插件作者,这个文件应该在 package.json 的 types 属性中被列出。为了利用模块扩展的优势,你需要确保将扩展的模块放在 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:tsimport { defineComponent } from 'vue'

export default defineComponent({

beforeRouteEnter(to, from, next) {

// ...

}

})如果没有确切的类型标注,这个钩子函数的参数会隐式地标注为 any 类型。我们可以为 ComponentCustomOptions 接口扩展自定义的选项来支持:tsimport { Route } from 'vue-router'

declare module 'vue' {

interface ComponentCustomOptions {

beforeRouteEnter?(to: Route, from: Route, next: () => void): void

}

}现在这个 beforeRouteEnter 选项会被准确地标注类型。注意这只是一个例子——像 vue-router 这种类型完备的库应该在它们自己的类型定义中自动执行这些扩展。这种类型扩展和全局属性扩展受到相同的限制。参考:对组件类型扩展的 TypeScript 单元测试在 GitHub 上编辑此页 前一篇TS 与组合式 API下一篇 使用 Vue 的多种方式TypeScript 与选项式 API已经加载完毕

TypeScript 与组合式 API | Vue.js

TypeScript 与组合式 API | Vue.js

直接跳到内容Vue.js搜索主导航文档 深度指南互动教程示例快速上手术语表错误码参照表Vue 2 文档从 Vue 2 迁移API演练场生态系统 资源合作伙伴主题UI 组件证书找工作T-Shirt 商店官方库Vue RouterPinia工具链指南视频课程Vue MasteryVue School帮助Discord 聊天室GitHub 论坛DEV Community动态博客Twitter活动新闻简报关于 常见问题团队版本发布社区指南行为规范纪录片赞助合作伙伴English 日本語 Українська Français 한국어 Português বাংলা Italiano 帮助我们翻译!githubtwitterdiscord外观githubtwitterdiscord菜单本页目录 API 风格偏好选项式组合式?侧边栏导航开始简介快速上手基础创建一个应用模板语法响应式基础计算属性类与样式绑定条件渲染列表渲染事件处理表单输入绑定生命周期侦听器模板引用组件基础深入组件注册Props事件组件 v-model透传 Attributes插槽依赖注入异步组件逻辑复用组合式函数自定义指令插件内置组件TransitionTransitionGroupKeepAliveTeleportSuspense应用规模化单文件组件工具链路由状态管理测试服务端渲染 (SSR)最佳实践生产部署性能优化无障碍访问安全TypeScript总览TS 与组合式 APITS 与选项式 API进阶主题使用 Vue 的多种方式组合式 API 常见问答深入响应式系统渲染机制渲染函数 & JSXVue 与 Web Components动画技巧本页目录当前页面的目录为组件的 props 标注类型为组件的 emits 标注类型为 ref() 标注类型为 reactive() 标注类型为 computed() 标注类型为事件处理函数标注类型为 provide / inject 标注类型为模板引用标注类型为组件模板引用标注类型赞助位成为赞助商TypeScript 与组合式 API ​这一章假设你已经阅读了搭配 TypeScript 使用 Vue 的概览。为组件的 props 标注类型 ​使用 这被称之为“运行时声明”,因为传递给 defineProps() 的参数会作为运行时的 props 选项使用。然而,通过泛型参数来定义 props 的类型通常更直接:vue这被称之为“基于类型的声明”。编译器会尽可能地尝试根据类型参数推导出等价的运行时选项。在这种场景下,我们第二个例子中编译出的运行时选项和第一个是完全一致的。基于类型的声明或者运行时声明可以择一使用,但是不能同时使用。我们也可以将 props 的类型移入一个单独的接口中:vue这同样适用于 Props 从另一个源文件中导入的情况。该功能要求 TypeScript 作为 Vue 的一个 peer dependency。vue语法限制 ​在 3.2 及以下版本中,defineProps() 的泛型类型参数仅限于类型文字或对本地接口的引用。这个限制在 3.3 中得到了解决。最新版本的 Vue 支持在类型参数位置引用导入和有限的复杂类型。但是,由于类型到运行时转换仍然基于 AST,一些需要实际类型分析的复杂类型,例如条件类型,还未支持。您可以使用条件类型来指定单个 prop 的类型,但不能用于整个 props 对象的类型。Props 解构默认值 ​当使用基于类型的声明时,我们失去了为 props 声明默认值的能力。这可以通过 withDefaults 编译器宏解决:tsexport interface Props {

msg?: string

labels?: string[]

}

const props = withDefaults(defineProps(), {

msg: 'hello',

labels: () => ['one', 'two']

})这将被编译为等效的运行时 props default 选项。此外,withDefaults 帮助程序为默认值提供类型检查,并确保返回的 props 类型删除了已声明默认值的属性的可选标志。非 对于运行时声明,我们可以使用 PropType 工具类型:tsimport type { PropType } from 'vue'

const props = defineProps({

book: Object as PropType

})其工作方式与直接指定 props 选项基本相同:tsimport { defineComponent } from 'vue'

import type { PropType } from 'vue'

export default defineComponent({

props: {

book: Object as PropType

}

})props 选项通常用于 Options API,因此你会在选项式 API 与 TypeScript 指南中找到更详细的例子。这些例子中展示的技术也适用于使用 defineProps() 的运行时声明。为组件的 emits 标注类型 ​在 类型参数可以是以下的一种:一个可调用的函数类型,但是写作一个包含调用签名的类型字面量。它将被用作返回的 emit 函数的类型。一个类型字面量,其中键是事件名称,值是数组或元组类型,表示事件的附加接受参数。上面的示例使用了具名元组,因此每个参数都可以有一个显式的名称。我们可以看到,基于类型的声明使我们可以对所触发事件的类型进行更细粒度的控制。若没有使用

没有类型标注时,这个 event 参数会隐式地标注为 any 类型。这也会在 tsconfig.json 中配置了 "strict": true 或 "noImplicitAny": true 时报出一个 TS 错误。因此,建议显式地为事件处理函数的参数标注类型。此外,你在访问 event 上的属性时可能需要使用类型断言:tsfunction handleChange(event: Event) {

console.log((event.target as HTMLInputElement).value)

}为 provide / inject 标注类型 ​provide 和 inject 通常会在不同的组件中运行。要正确地为注入的值标记类型,Vue 提供了一个 InjectionKey 接口,它是一个继承自 Symbol 的泛型类型,可以用来在提供者和消费者之间同步注入值的类型:tsimport { provide, inject } from 'vue'

import type { InjectionKey } from 'vue'

const key = Symbol() as InjectionKey

provide(key, 'foo') // 若提供的是非字符串值会导致错误

const foo = inject(key) // foo 的类型:string | undefined建议将注入 key 的类型放在一个单独的文件中,这样它就可以被多个组件导入。当使用字符串注入 key 时,注入值的类型是 unknown,需要通过泛型参数显式声明:tsconst foo = inject('foo') // 类型:string | undefined注意注入的值仍然可以是 undefined,因为无法保证提供者一定会在运行时 provide 这个值。当提供了一个默认值后,这个 undefined 类型就可以被移除:tsconst foo = inject('foo', 'bar') // 类型:string如果你确定该值将始终被提供,则还可以强制转换该值:tsconst foo = inject('foo') as string为模板引用标注类型 ​模板引用需要通过一个显式指定的泛型参数和一个初始值 null 来创建:vue

可以通过类似于 MDN 的页面来获取正确的 DOM 接口。注意为了严格的类型安全,有必要在访问 el.value 时使用可选链或类型守卫。这是因为直到组件被挂载前,这个 ref 的值都是初始的 null,并且在由于 v-if 的行为将引用的元素卸载时也可以被设置为 null。为组件模板引用标注类型 ​有时,你可能需要为一个子组件添加一个模板引用,以便调用它公开的方法。举例来说,我们有一个 MyModal 子组件,它有一个打开模态框的方法:vue

为了获取 MyModal 的类型,我们首先需要通过 typeof 得到其类型,再使用 TypeScript 内置的 InstanceType 工具类型来获取其实例类型:vue

注意,如果你想在 TypeScript 文件而不是在 Vue SFC 中使用这种技巧,需要开启 Volar 的 Takeover 模式。如果组件的具体类型无法获得,或者你并不关心组件的具体类型,那么可以使用 ComponentPublicInstance。这只会包含所有组件都共享的属性,比如 $el。tsimport { ref } from 'vue'

import type { ComponentPublicInstance } from 'vue'

const child = ref(null)在 GitHub 上编辑此页 前一篇总览下一篇 TS 与选项式 APITypeScript 与组合式 API已经加载完毕

Global API: General | Vue.js

Global API: General | Vue.js

Skip to contentVue.jsSearchMain NavigationDocs GuideTutorialExamplesQuick StartGlossaryError ReferenceVue 2 DocsMigration from Vue 2APIPlaygroundEcosystem ResourcesPartnersThemesUI ComponentsCertificationJobsT-Shirt ShopOfficial LibrariesVue RouterPiniaTooling GuideVideo CoursesVue MasteryVue SchoolHelpDiscord ChatGitHub DiscussionsDEV CommunityNewsBlogTwitterEventsNewslettersAbout FAQTeamReleasesCommunity GuideCode of ConductThe DocumentarySponsorPartners简体中文 日本語 Українська Français 한국어 Português বাংলা Italiano Help Us Translate!githubtwitterdiscordAppearancegithubtwitterdiscordMenuOn this page Sidebar NavigationGlobal APIApplicationGeneralComposition APIsetup()Reactivity: CoreReactivity: UtilitiesReactivity: AdvancedLifecycle HooksDependency InjectionOptions APIOptions: StateOptions: RenderingOptions: LifecycleOptions: CompositionOptions: MiscComponent InstanceBuilt-insDirectivesComponentsSpecial ElementsSpecial AttributesSingle-File ComponentSyntax Specification

vue

See also this.$nextTick()defineComponent() ​A type helper for defining a Vue component with type inference.Typets// options syntax

function defineComponent(

component: ComponentOptions

): ComponentConstructor

// function syntax (requires 3.3+)

function defineComponent(

setup: ComponentOptions['setup'],

extraOptions?: ComponentOptions

): () => anyType is simplified for readability.DetailsThe first argument expects a component options object. The return value will be the same options object, since the function is essentially a runtime no-op for type inference purposes only.Note that the return type is a bit special: it will be a constructor type whose instance type is the inferred component instance type based on the options. This is used for type inference when the returned type is used as a tag in TSX.You can extract the instance type of a component (equivalent to the type of this in its options) from the return type of defineComponent() like this:tsconst Foo = defineComponent(/* ... */)

type FooInstance = InstanceTypeFunction Signature ​defineComponent() also has an alternative signature that is meant to be used with Composition API and render functions or JSX.Instead of passing in an options object, a function is expected instead. This function works the same as the Composition API setup() function: it receives the props and the setup context. The return value should be a render function - both h() and JSX are supported:jsimport { ref, h } from 'vue'

const Comp = defineComponent(

(props) => {

// use Composition API here like in