Skip to content

Commit

Permalink
docs: translate /guide/typescript/options-api
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaodong2008 committed Apr 24, 2024
1 parent 5c7c28b commit 5010326
Showing 1 changed file with 49 additions and 49 deletions.
98 changes: 49 additions & 49 deletions src/guide/typescript/options-api.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
# TypeScript 与选项式 API {#typescript-with-options-api}
# TypeScript 與選項式 API {#typescript-with-options-api}

> 这一章假设你已经阅读了[搭配 TypeScript 使用 Vue](./overview) 的概览
> 這一章假設你已經閱讀了[搭配 TypeScript 使用 Vue](./overview) 的概覽
:::tip
虽然 Vue 的确支持在选项式 API 中使用 TypeScript,但在使用 TypeScript 的前提下更推荐使用组合式 API,因为它提供了更简单、高效和可靠的类型推导
雖然 Vue 的確支持在選項式 API 中使用 TypeScript,但在使用 TypeScript 的前提下更推薦使用組合式 API,因為它提供了更簡單、高效和可靠的類型推導
:::

## 为组件的 props 标注类型 {#typing-component-props}
## 為組件的 props 標註類型 {#typing-component-props}

选项式 API 中对 props 的类型推导需要用 `defineComponent()` 来包装组件。有了它,Vue 才可以通过 `props` 以及一些额外的选项,比如 `required: true``default` 来推导出 props 的类型
選項式 API 中對 props 的類型推導需要用 `defineComponent()` 來包裝組件。有了它,Vue 才可以通過 `props` 以及一些額外的選項,例如 `required: true``default` 來推導出 props 的類型

```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
this.name // 類型:string | undefined
this.id // 類型:number | string | undefined
this.msg // 類型:string
this.metadata // 類型:any
}
})
```

然而,这种运行时 `props` 选项仅支持使用构造函数来作为一个 prop 的类型——没有办法指定多层级对象或函数签名之类的复杂类型
然而,這種運行時 `props` 選項僅支持使用構造函數來作為一個 prop 的類型——沒有辦法指定多層級對象或函數簽名之類的複雜類型

我们可以使用 `PropType` 这个工具类型来标记更复杂的 props 类型
我們可以使用 `PropType` 這個工具類型來標記更復雜的 props 類型

```ts
import { defineComponent } from 'vue'
Expand All @@ -47,11 +47,11 @@ interface Book {
export default defineComponent({
props: {
book: {
// 提供相对 `Object` 更确定的类型
// 提供相對 `Object` 更確定的類型
type: Object as PropType<Book>,
required: true
},
// 也可以标记函数
// 也可以標記函數
callback: Function as PropType<(id: number) => void>
},
mounted() {
Expand All @@ -65,9 +65,9 @@ export default defineComponent({
})
```

### 注意事项 {#caveats}
### 注意事項 {#caveats}

如果你的 TypeScript 版本低于 `4.7`在使用函数作为 prop 的 `validator``default` 选项值时需要格外小心——确保使用箭头函数
如果你的 TypeScript 版本低於 `4.7`在使用函數作為 prop 的 `validator``default` 選項值時需要格外小心——確保使用箭頭函數

```ts
import { defineComponent } from 'vue'
Expand All @@ -82,7 +82,7 @@ export default defineComponent({
props: {
bookA: {
type: Object as PropType<Book>,
// 如果你的 TypeScript 版本低于 4.7,确保使用箭头函数
// 如果你的 TypeScript 版本低於 4.7,確保使用箭頭函數
default: () => ({
title: 'Arrow Function Expression'
}),
Expand All @@ -92,37 +92,37 @@ export default defineComponent({
})
```

这会防止 TypeScript `this` 根据函数内的环境作出不符合我们期望的类型推导。这是之前版本的一个[设计限制](https://github.com/microsoft/TypeScript/issues/38845)不过现在已经在 [TypeScript 4.7](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#improved-function-inference-in-objects-and-methods) 中解决了
這會防止 TypeScript `this` 根據函數內的環境作出不符合我們期望的類型推導。這是之前版本的一個[設計限制](https://github.com/microsoft/TypeScript/issues/38845)不過現在已經在 [TypeScript 4.7](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#improved-function-inference-in-objects-and-methods) 中解決了

## 为组件的 emits 标注类型 {#typing-component-emits}
## 為組件的 emits 標註類型 {#typing-component-emits}

我们可以给 `emits` 选项提供一个对象来声明组件所触发的事件,以及这些事件所期望的参数类型。试图触发未声明的事件会抛出一个类型错误
我們可以給 `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 // 类型错误
bookName: 123 // 類型錯誤
})

this.$emit('non-declared-event') // 类型错误
this.$emit('non-declared-event') // 類型錯誤
}
}
})
```

## 为计算属性标记类型 {#typing-computed-properties}
## 為計算屬性標記類型 {#typing-computed-properties}

计算属性会自动根据其返回值来推导其类型
計算屬性會自動根據其返回值來推導其類型

```ts
import { defineComponent } from 'vue'
Expand All @@ -139,12 +139,12 @@ export default defineComponent({
}
},
mounted() {
this.greeting // 类型:string
this.greeting // 類型:string
}
})
```

在某些场景中,你可能想要显式地标记出计算属性的类型以确保其实现是正确的
在某些場景中,你可能想要顯式地標記出計算屬性的類型以確保其實現是正確的

```ts
import { defineComponent } from 'vue'
Expand All @@ -156,12 +156,12 @@ export default defineComponent({
}
},
computed: {
// 显式标注返回类型
// 顯式標註返回類型
greeting(): string {
return this.message + '!'
},

// 标注一个可写的计算属性
// 標註一個可寫的計算屬性
greetingUppercased: {
get(): string {
return this.greeting.toUpperCase()
Expand All @@ -174,11 +174,11 @@ export default defineComponent({
})
```

在某些 TypeScript 因循环引用而无法推导类型的情况下,可能必须进行显式的类型标注
在某些 TypeScript 因循環引用而無法推導類型的情況下,可能必須進行顯式的類型標註

## 为事件处理函数标注类型 {#typing-event-handlers}
## 為事件處理函數標註類型 {#typing-event-handlers}

在处理原生 DOM 事件时,应该为我们传递给事件处理函数的参数正确地标注类型。让我们看一下这个例子
在處理原生 DOM 事件時,應該為我們傳遞給事件處理函數的參數正確地標註類型。讓我們看一下這個例子

```vue
<script lang="ts">
Expand All @@ -187,7 +187,7 @@ import { defineComponent } from 'vue'
export default defineComponent({
methods: {
handleChange(event) {
// `event` 隐式地标注为 `any` 类型
// `event` 隱式地標註為 `any` 類型
console.log(event.target.value)
}
}
Expand All @@ -199,7 +199,7 @@ export default defineComponent({
</template>
```

没有类型标注时,这个 `event` 参数会隐式地标注为 `any` 类型。这也会在 `tsconfig.json` 中配置了 `"strict": true``"noImplicitAny": true` 时抛出一个 TS 错误。因此,建议显式地为事件处理函数的参数标注类型。此外,在访问 `event` 上的属性时你可能需要使用类型断言
沒有類型標註時,這個 `event` 參數會隱式地標註為 `any` 類型。這也會在 `tsconfig.json` 中配置了 `"strict": true``"noImplicitAny": true` 時拋出一個 TS 錯誤。因此,建議顯式地為事件處理函數的參數標註類型。此外,在訪問 `event` 上的屬性時你可能需要使用類型斷言

```ts
import { defineComponent } from 'vue'
Expand All @@ -213,9 +213,9 @@ export default defineComponent({
})
```

## 扩展全局属性 {#augmenting-global-properties}
## 擴展全局屬性 {#augmenting-global-properties}

某些插件会通过 [`app.config.globalProperties`](/api/application#app-config-globalproperties) 为所有组件都安装全局可用的属性。举例来说,我们可能为了请求数据而安装了 `this.$http`或者为了国际化而安装了 `this.$translate`为了使 TypeScript 更好地支持这个行为,Vue 暴露了一个被设计为可以通过 [TypeScript 模块扩展](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation)来扩展的 `ComponentCustomProperties` 接口:
某些插件會通過 [`app.config.globalProperties`](/api/application#app-config-globalproperties) 為所有組件都安裝全局可用的屬性。舉例來說,我們可能為了請求數據而安裝了 `this.$http`或者為了國際化而安裝了 `this.$translate`為了使 TypeScript 更好地支持這個行為,Vue 暴露了一個被設計為可以通過 [TypeScript 模塊擴展](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation)來擴展的 `ComponentCustomProperties` 接口:

```ts
import axios from 'axios'
Expand All @@ -228,18 +228,18 @@ declare module 'vue' {
}
```

参考
參考

- [对组件类型扩展的 TypeScript 单元测试](https://github.com/vuejs/core/blob/main/packages/dts-test/componentTypeExtensions.test-d.tsx)
- [對組件類型擴展的 TypeScript 單元測試](https://github.com/vuejs/core/blob/main/packages/dts-test/componentTypeExtensions.test-d.tsx)

### 类型扩展的位置 {#type-augmentation-placement}
### 類型擴展的位置 {#type-augmentation-placement}

我们可以将这些类型扩展放在一个 `.ts` 文件,或是一个影响整个项目的 `*.d.ts` 文件中。无论哪一种,都应确保在 `tsconfig.json` 中包括了此文件。对于库或插件作者,这个文件应该在 `package.json``types` 属性中被列出
我們可以將這些類型擴展放在一個 `.ts` 文件,或是一個影響整個項目的 `*.d.ts` 文件中。無論哪一種,都應確保在 `tsconfig.json` 中包括了此文件。對於庫或插件作者,這個文件應該在 `package.json``types` 屬性中被列出

为了利用模块扩展的优势,你需要确保将扩展的模块放在 [TypeScript 模块](https://www.typescriptlang.org/docs/handbook/modules.html) 中。 也就是说,该文件需要包含至少一个顶级的 `import``export`,即使它只是 `export {}`如果扩展被放在模块之外,它将覆盖原始类型,而不是扩展!
為了利用模塊擴展的優勢,你需要確保將擴展的模塊放在 [TypeScript 模塊](https://www.typescriptlang.org/docs/handbook/modules.html) 中。 也就是說,該文件需要包含至少一個頂級的 `import``export`,即使它只是 `export {}`如果擴展被放在模塊之外,它將覆蓋原始類型,而不是擴展!

```ts
// 不工作,将覆盖原始类型
// 不工作,將覆蓋原始類型
declare module 'vue' {
interface ComponentCustomProperties {
$translate: (key: string) => string
Expand All @@ -258,9 +258,9 @@ declare module 'vue' {
}
```

## 扩展自定义选项 {#augmenting-custom-options}
## 擴展自定義選項 {#augmenting-custom-options}

某些插件,比如 `vue-router`提供了一些自定义的组件选项,比如 `beforeRouteEnter`
某些插件,例如 `vue-router`提供了一些自定義的組件選項,例如 `beforeRouteEnter`

```ts
import { defineComponent } from 'vue'
Expand All @@ -272,7 +272,7 @@ export default defineComponent({
})
```

如果没有确切的类型标注,这个钩子函数的参数会隐式地标注为 `any` 类型。我们可以为 `ComponentCustomOptions` 接口扩展自定义的选项来支持
如果沒有確切的類型標註,這個鉤子函數的參數會隱式地標註為 `any` 類型。我們可以為 `ComponentCustomOptions` 接口擴展自定義的選項來支持

```ts
import { Route } from 'vue-router'
Expand All @@ -284,12 +284,12 @@ declare module 'vue' {
}
```

现在这个 `beforeRouteEnter` 选项会被准确地标注类型。注意这只是一个例子——像 `vue-router` 这种类型完备的库应该在它们自己的类型定义中自动执行这些扩展
現在這個 `beforeRouteEnter` 選項會被準確地標註類型。注意這只是一個例子——像 `vue-router` 這種類型完備的庫應該在它們自己的類型定義中自動執行這些擴展

这种类型扩展和全局属性扩展受到[相同的限制](#type-augmentation-placement)
這種類型擴展和全局屬性擴展受到[相同的限制](#type-augmentation-placement)

参考
參考

- [对组件类型扩展的 TypeScript 单元测试](https://github.com/vuejs/core/blob/main/packages/dts-test/componentTypeExtensions.test-d.tsx)
- [對組件類型擴展的 TypeScript 單元測試](https://github.com/vuejs/core/blob/main/packages/dts-test/componentTypeExtensions.test-d.tsx)

<!-- zhlint disabled -->

0 comments on commit 5010326

Please sign in to comment.