1. 使用 const 断言锁定类型,确保对象的属性不可更改。
可能你们在编码的时候也遇到过这样的情况,我们定义的接口类型被莫名其妙的修改了导致代码的报错,这样的情况会浪费我们很多不必要的时间来排查bug。那我们应该如何杜绝这种情况的发生呢,这时 const 断言就派上用场了!使用 as const
后,TypeScript 可以确保我们定义的接口类型在后续代码中不会发生变化。这就像为变量加上“请勿触摸”的标志一样,以确保其安全。
const colors = ['red', 'green', 'blue'] as const;
// 没有 `as const` 时,类型是 `string[]`
let colors1 = ['red', 'green', 'blue'];
// colors1[0] = 'yellow'; // 允许
// 使用 `as const` 后,类型是 `readonly ["red", "green", "blue"]`
const colors2 = ['red', 'green', 'blue'] as const;
// colors2[0] = 'yellow'; // 错误,不能修改数组的元素
当我们使用 as const 时,TypeScript 会进行以下操作:
-
将数组或对象中的每个元素或属性标记为 readonly:
- 对于数组,使用 as const 后,每个元素都会变为只读,数组本身也会变为只读。
- 对于对象,使用 as const 后,每个属性都会变为只读。
-
推断出最具体的字面量类型:
- 不使用 as const 时,TypeScript 通常会推断出更宽泛的类型。
- 使用 as const 后,TypeScript 会推断出最具体的字面量类型。
2. 利用 Pick 创建自定义类型,从大型类型中选择需要的部分。
假如我们已经定义过了一个拥有很多类型的A接口,但是我们在后续的代码操作中还需求一个新的B接口,但是新的B接口里的类型已经在之前的A接口里定义过一次了,(A>B),我们的B接口只需要A接口的其中的一类型部分。
这时我们就可以使用 Pick 来帮助我们实现,减少代码里的重复定义。我们可以创建一个仅选择所需内容的新接口。就像我们在超市买散装糖果有一些,我们只需挑选我们想要吃的口味的糖果就行,而无需为了某种口味,而整包购买。
interface User { // 定义一个接口 User
id: number; // 用户 ID
name: string; // 用户名
email: string; // 用户邮箱
}
type UserSummary = Pick<User, 'name' | 'email'>; // 使用 Pick 从 User 类型中选择 'name' 和 'email' 属性,创建新类型 UserSummary
const user: User = { // 定义一个 User 类型的对象 user
id: 1,
name: 'John Doe',
email: 'john.doe@example.com'
};
const summary: UserSummary = { // 定义一个 UserSummary 类型的对象 summary
name: user.name, // 从 user 对象中获取 name 属性
email: user.email // 从 user 对象中获取 email 属性
};
3. 使用 Extract 缩小选项范围,从联合类型中提取特定的选项。
当我们已经拥有一个联合类型了,而我们又需要创建一个被联合类型所包含的类型时,Extract
就起作用了。
Extract
的作用就是从一个联合类型(union type)中提取符合指定条件的子类型
type A = string | number | boolean;
type B = Extract<A, string | number>; // B 是 string | number
type Animal = 'dog' | 'cat' | 'bird' | 'fish';
type Mammal = Extract<Animal, 'dog' | 'cat'>;
在上面这个例子中,A 是一个联合类型 string | number | boolean。我们使用 Extract<A, string | number> 提取出可以赋值给B的子类型,结果是 B 的类型就是string | number。
从接口类型中提取
interface Person {
name: string;
age: number;
isEmployee: boolean;
}
type StringKeys = Extract<keyof Person, string>; // StringKeys 是 'name'
//在这个例子中,我们使用 `keyof Person` 获取 `Person` 接口的所有键,然后用 `Extract` 提取出键为 `string` 类型的键,结果是 `'name'`。
提取特定类型的函数
type Func = (x: number) => string | ((x: string) => number) | ((x: boolean) => void);
type StringToNumberFunc = Extract<Func, (x: string) => any>;
// StringToNumberFunc 是 (x: string) => number
4. 使用 Readonly 保障数据安全,确保数据或对象的内容不可更改。
在没有使用TypeScript之前,我们的要把变量只读都是通过使用Object.defineProperty()函数将obj对象的pro1属性修改为不可写。这样就实现了只读变量。但是使用TypeScript之后,我们想要声明一个只读变量变的十分简单。只要使用Readonly
就行。
const fruits: ReadonlyArray<string> = ['apple', 'banana', 'cherry']; // 定义一个 ReadonlyArray 类型的数组 fruits,不可修改
// 这将导致 TypeScript 错误
// fruits.push('date'); // 尝试向 ReadonlyArray 添加新元素,导致 TypeScript 错误
// 这也将导致 TypeScript 错误
// fruits[1] = 'blueberry'; // 尝试修改 ReadonlyArray 中的元素,导致 TypeScript 错误
5. 使用keyof
获取对象的所有类型,获取类型的所有键
在开发时,我们可能也会遇到需要获取一个对象的所有类型的情景,这时我们就可以使keyof
来获取。
interface User { // 定义一个接口 User
id: number; // 用户 ID
name: string; // 用户名
email: string; // 用户邮箱
}
type UserKey = keyof User; // 使用 keyof 获取 User 类型的所有键,创建新类型 UserKey
//PersonKeys 是一个联合类型,它包含了 Person 类型的所有键
const key: UserKey = 'name'; // 定义一个 UserKey 类型的变量 key,赋值为 'name'
// 这将导致 TypeScript 错误
// const invalidKey: UserKey = 'age'; // 尝试将不存在于 User 类型中的键 'age' 赋值给 UserKey 类型的变量,导致 TypeScript 错误
假设我们想要创建一个类型,并且我们想把 User 类型中的所有属性都变成可选的,这时我们也可以使用keyof
type PartialPerson = {
[K in keyof User]?: User[K];
};
或者又是我们想创建一个类型,来提取对象中所有值类型为字符串的属性键
// 定义一个泛型类型 StringKeys,它接受一个类型参数 T
type StringKeys<T> = {
// 遍历 T 的所有键 K,并生成一个新的类型
[K in keyof T]:
// 检查 T[K] 是否为 string 类型
T[K] extends string
// 如果是,则返回键 K
? K
// 如果不是,则返回 never
: never
// 取出所有键的联合类型
}[keyof T];
// 使用 StringKeys 类型来提取 Person 类型中所有值为 string 的键
type PersonStringKeys = StringKeys<Person>; // "name" | "address"