TypeScript装饰器与高级类型:映射类型和条件类型详解
立即解锁
发布时间: 2025-09-05 00:12:27 阅读量: 4 订阅数: 13 AIGC 

# TypeScript 装饰器与高级类型:映射类型和条件类型详解
## 1. 映射类型概述
映射类型允许我们通过对现有类型应用转换函数来创建新类型。下面将详细介绍几种常见的映射类型及其应用。
### 1.1 Readonly 映射类型
假设我们有一个 `Person` 接口,并且需要将 `Person` 类型的对象传递给 `doStuff()` 函数进行处理,但不希望该函数意外修改 `Person` 对象的某些属性,例如 `age` 属性。
```typescript
interface Person {
name: string;
age: number;
}
const worker: Person = { name: "John", age: 22 };
function doStuff(person: Person) {
person.age = 25;
}
```
在上述代码中,`Person` 类型的属性没有使用 `readonly` 修饰符。我们可以使用内置的 `Readonly` 映射类型将之前声明的类型的所有属性转换为只读属性。只需将 `doStuff()` 函数的签名修改为接受 `Readonly<Person>` 类型的参数,而不是 `Person` 类型。
```typescript
const worker: Person = { name: "John", age: 22 };
function doStuff(person: Readonly<Person>) {
person.age = 25; // 这行代码会产生编译器错误
}
```
要理解为什么尝试更改 `age` 属性的值会产生编译器错误,我们需要了解 `Readonly` 类型的声明,这需要对 `keyof` 查找类型有一定的了解。
#### 1.1.1 KEYOF 和查找类型
`keyof` 也称为索引类型查询,它表示给定类型的允许属性名称(键)的联合。如果 `Person` 类型是我们的 `T`,那么 `keyof T` 将表示 `name` 和 `age` 的联合。
`Readonly` 映射函数在 `lib.es5.d.ts` 中的声明如下:
```typescript
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
```
在上述代码中,`[P in keyof T]` 表示“给我给定类型 `T` 的所有属性的联合”,`T[P]` 是一个查找类型,表示“给我属性 `P` 的类型”。
`Readonly<Person>` 将创建一个映射类型,看起来如下:
```typescript
interface Person {
readonly name: string;
readonly age: number;
}
```
因此,尝试修改 `person` 的 `age` 属性会导致编译器错误“Cannot assign to age because it’s a read-only property”。
#### 1.1.2 Readonly 映射类型的实际应用
在某些场景中,我们可能需要确保传递给函数的对象不会被意外修改。例如,在一个消息处理函数中,为了防止意外修改消息内容,我们可以使用 `Readonly` 映射类型。
```typescript
function replyTo(client: WebSocket, message: Readonly<T>): void {
// 处理消息
}
```
#### 1.1.3 使用 keyof 和查找类型进行类型检查
考虑一个过滤通用对象数组的函数,只保留在指定属性中具有指定值的对象。
```typescript
function filterBy<T>(
property: any,
value: any,
array: T[]
) {
return array.filter(item => item[property] === value);
}
interface Person {
name: string;
age: number;
}
const persons: Person[] = [
{ name: 'John', age: 32 },
{ name: 'Mary', age: 33 },
];
console.log(filterBy('name', 'John', persons));
console.log(filterBy('lastName', 'John', persons));
console.log(filterBy('age', 'twenty', persons));
```
上述代码中,调用 `filterBy` 函数时使用不存在的属性名或错误的值类型会导致难以发现的错误。我们可以修改 `filterBy` 函数的签名,以在编译时捕获这些错误。
```typescript
function filterBy<T, P extends keyof T>(
property: P,
value: T[P],
array: T[]
) {
return array.filter(item => item[property] === value);
}
```
在上述代码中,`<T, P extends keyof T>` 表示函数接受两个泛型值 `T` 和 `P`,并且 `P` 必须是 `T` 类型的属性之一。`value: T[P]` 表示提供的值必须与 `T` 类型中为 `P` 声明的类型相同。因此,以下代码会产生编译错误:
```typescript
filterBy('lastName', 'John', persons);
filterBy('age', 'twenty', persons);
```
### 1.2 声明自己的映射类型
我们可以使用类似的语法定义自己的转换函数。例如,定义一个 `Modifiable` 类型,它是 `Readonly` 类型的相反类型。
```typescript
interface Person {
readonly name: string;
readonly age: number;
}
type Modifiable<T> = {
-readonly [P in keyof T]: T[P];
};
const worker1: Person = { name: "John", age: 25 };
worker1.age = 27; // 这行代码会产生编译器错误
const worker2: Modifiable<Person> = { name: "John", age: 25 };
worker2.age = 27; // 这行代码不会产生编译器错误
```
在上述代码中,`-readonly` 表示从给定类型的所有属性中移除 `readonly` 修饰符。
### 1.3 其他内置映射类型
#### 1.3.1 Partial 映射类型
如果我们需要一个类型,它具有与 `Person` 类型相同的属性,但所有属性都是可选的,我们可以使用
0
0
复制全文
相关推荐









