十个高级开发者需要知道的TypeScript 技巧
文章目录
- keyof 关键字提供了一种基于现有类型的键动态构建类型的强大工具。 “keyof 运算符采用对象类型并生成其键的字符串或数字文字联合。” — TypeScript 文档 结合映射类型,您可以从现有类型生成新类型,同时保留原始结构。 type Point = { x: number; y: number }; type NullablePoint = { [K in keyof Point]: Point[K] | null; }; 在这里,NullablePoint 成为一种新类型,具有与 Point 相同的键,但具有可为空的值。
- 条件类型允许创建依赖于其他类型的复杂类型。 这可以导致更灵活和可重用的类型定义。 type Flatten<T> = T extends Array<infer U> ? U : T; type FlattenedNumbers = Flatten<number[]>; // number 在此示例中,Flatten 类型检查提供的类型 T 是否为数组,如果是,则提取内部类型 U。
- TypeScript 对高阶类型的支持允许您创建部分应用的类型。 这种称为类型套用的技术可实现强大的组合模式。 type Curried<T, U> = (arg: U) => T & U; function merge<T, U>(fn: Curried<T, U>): T & U { return fn({} as U); } type UserDetails = { firstName: string; lastName: string }; type UserSettings = { theme: string; language: string }; const result = merge<UserDetails, UserSettings>(() => ({ firstName: 'John', lastName: 'Doe', theme: 'dark', language: 'en', })); console.log(result); /** { "firstName": "John", "lastName": "Doe", "theme": "dark", "language": "en" } */ 在此示例中,我们有两种不同类型的对象,UserDetails 和 UserSettings。 通过使用 merge 函数,我们可以将这两种类型组合成一个包含两种类型属性的对象。 当您想要创建一个新对象来组合来自多个来源的属性,同时仍保持 TypeScript 类型完整时,这会很有用。 当然,在 TypeScript 中还有其他方法可以实现这一点,例如,使用类型交集和直接扩展语法。 此示例的主要目的是演示类型柯里化的概念,当在正确的上下文中应用时,它可以成为一种强大的技术。
- 当您需要执行编译时类型检查时,类型保护很有用。 它们允许 TypeScript 缩小特定代码块中值的类型。 function isString(value: any): value is string { return typeof value === "string"; } function concat(a: unknown, b: unknown) { if (isString(a) && isString(b)) { return a.concat(b); } } 在 concat 函数中,TypeScript 知道 a 和 b 是字符串,因为类型保护是 isString。 这也可以改进 VS Code Intellisense 的提示,因为编译器将能够约束任何类型。
- Return Type 实用程序类型可以推断函数的返回类型,从而更容易使用高阶函数及其类型。 type MyFunction = (x: number, y: number) => { result: number }; type MyFunctionReturnType = ReturnType<MyFunction>; // { result: number } 在这里,MyFunctionReturnType 成为 MyFunction 的推断返回类型。
- TypeScript 支持递归类型,允许您创建复杂的类型级计算和转换。 type TupleToUnion<T extends any[]> = T[number]; type MyTuple = [string, number, boolean]; type MyUnion = TupleToUnion<MyTuple>; // string | number | boolean 在此示例中,TupleToUnion 将元组类型转换为联合类型,这在处理复杂数据结构时非常有用。 就个人而言,我更喜欢联合类型而不是枚举,以至于我会将我看到的任何枚举重构为联合类型。 智能感知更好!
- TypeScript 提供了一个内置的 Readonly 实用程序类型,它使对象的所有属性都成为只读的。 当您想在代码库中强制执行不变性时,这很有用。 type User = { name: string; age: number }; type ReadonlyUser = Readonly<User>; const user: ReadonlyUser = { name: "Alice", age: 30 }; user.age = 31; // Error: Cannot assign to 'age' because it is a read-only property 通过使用 Readonly 实用程序,我们确保用户对象不会发生变化。
- 当您对值的类型的了解比 TypeScript 的类型推断所能推断的更多时,类型断言很有用。 它们允许您为值指定更精确的类型,而无需执行任何运行时检查。 const unknownValue: unknown = "hello world"; const stringValue: string = unknownValue as string; 在此示例中,我们断言 unknownValue 确实是一个字符串,TypeScript 将信任此断言。
- TypeScript 独特的符号类型可以创建独特的非字符串值,非常适合更安全的常量定义和避免名称冲突。 const MyConstant = Symbol("MyConstant") as unique symbol; type MyType = { [MyConstant]: string; }; const obj: MyType = { [MyConstant]: "hello world" }; console.log(obj[MyConstant]); // "hello world" 在这里,MyConstant 是一个独特的符号,确保没有其他属性可以与它冲突。
- TypeScript 允许合并类型声明,这在跨多个文件拆分类型定义或从外部库扩展类型时非常有用。 // file1.ts interface Point { x: number; y: number; } // file2.ts interface Point { z: number; } // Merged Point type: { x: number; y: number; z: number } 通过在 file1.ts 和 file2.ts 中声明 Point,TypeScript 会将两个声明合并为一个类型。 我认为这种行为只发生在 interface 关键字上,而不是 type 关键字,所以我更喜欢 type。 这 10 个 TypeScript 提示和技巧应该可以帮助您提升 TypeScript 技能并编写更强大、灵活和可维护的代码。 不要害怕探索 TypeScript 的高级功能,并在您的项目中利用它们来实现更干净、更安全的代码。
TypeScript 已迅速成为像我这样的开发人员的首选语言,他们希望通过添加类型安全、更好的工具和改进的可维护性来改进 JavaScript 代码库。

随着语言的发展和成熟,TypeScript 沿途获得了一些隐藏的功能。 在本文中,我们将深入探讨 10 个鲜为人知的技巧,它们将帮助您释放 TypeScript 的全部潜力。
keyof 关键字提供了一种基于现有类型的键动态构建类型的强大工具。
“keyof 运算符采用对象类型并生成其键的字符串或数字文字联合。”
— TypeScript 文档
结合映射类型,您可以从现有类型生成新类型,同时保留原始结构。
type Point = { x: number; y: number };
type NullablePoint = {
[K in keyof Point]: Point[K] | null;
};
在这里,NullablePoint 成为一种新类型,具有与 Point 相同的键,但具有可为空的值。
条件类型允许创建依赖于其他类型的复杂类型。 这可以导致更灵活和可重用的类型定义。
type Flatten<T> = T extends Array<infer U> ? U : T;
type FlattenedNumbers = Flatten<number[]>; // number
在此示例中,Flatten 类型检查提供的类型 T 是否为数组,如果是,则提取内部类型 U。
TypeScript 对高阶类型的支持允许您创建部分应用的类型。 这种称为类型套用的技术可实现强大的组合模式。
type Curried<T, U> = (arg: U) => T & U;
function merge<T, U>(fn: Curried<T, U>): T & U {
return fn({} as U);
}
type UserDetails = { firstName: string; lastName: string };
type UserSettings = { theme: string; language: string };
const result = merge<UserDetails, UserSettings>(() => ({
firstName: 'John',
lastName: 'Doe',
theme: 'dark',
language: 'en',
}));
console.log(result);
/** {
"firstName": "John",
"lastName": "Doe",
"theme": "dark",
"language": "en"
} */
在此示例中,我们有两种不同类型的对象,UserDetails 和 UserSettings。 通过使用 merge 函数,我们可以将这两种类型组合成一个包含两种类型属性的对象。 当您想要创建一个新对象来组合来自多个来源的属性,同时仍保持 TypeScript 类型完整时,这会很有用。
当然,在 TypeScript 中还有其他方法可以实现这一点,例如,使用类型交集和直接扩展语法。 此示例的主要目的是演示类型柯里化的概念,当在正确的上下文中应用时,它可以成为一种强大的技术。
当您需要执行编译时类型检查时,类型保护很有用。 它们允许 TypeScript 缩小特定代码块中值的类型。
function isString(value: any): value is string {
return typeof value === "string";
}
function concat(a: unknown, b: unknown) {
if (isString(a) && isString(b)) {
return a.concat(b);
}
}
在 concat 函数中,TypeScript 知道 a 和 b 是字符串,因为类型保护是 isString。 这也可以改进 VS Code Intellisense 的提示,因为编译器将能够约束任何类型。
Return Type 实用程序类型可以推断函数的返回类型,从而更容易使用高阶函数及其类型。
type MyFunction = (x: number, y: number) => { result: number };
type MyFunctionReturnType = ReturnType<MyFunction>; // { result: number }
在这里,MyFunctionReturnType 成为 MyFunction 的推断返回类型。
TypeScript 支持递归类型,允许您创建复杂的类型级计算和转换。
type TupleToUnion<T extends any[]> = T[number];
type MyTuple = [string, number, boolean];
type MyUnion = TupleToUnion<MyTuple>; // string | number | boolean
在此示例中,TupleToUnion 将元组类型转换为联合类型,这在处理复杂数据结构时非常有用。
就个人而言,我更喜欢联合类型而不是枚举,以至于我会将我看到的任何枚举重构为联合类型。 智能感知更好!
TypeScript 提供了一个内置的 Readonly 实用程序类型,它使对象的所有属性都成为只读的。 当您想在代码库中强制执行不变性时,这很有用。
type User = { name: string; age: number };
type ReadonlyUser = Readonly<User>;
const user: ReadonlyUser = { name: "Alice", age: 30 };
user.age = 31;
// Error: Cannot assign to 'age' because it is a read-only property
通过使用 Readonly 实用程序,我们确保用户对象不会发生变化。
当您对值的类型的了解比 TypeScript 的类型推断所能推断的更多时,类型断言很有用。 它们允许您为值指定更精确的类型,而无需执行任何运行时检查。
const unknownValue: unknown = "hello world";
const stringValue: string = unknownValue as string;
在此示例中,我们断言 unknownValue 确实是一个字符串,TypeScript 将信任此断言。
TypeScript 独特的符号类型可以创建独特的非字符串值,非常适合更安全的常量定义和避免名称冲突。
const MyConstant = Symbol("MyConstant") as unique symbol;
type MyType = {
[MyConstant]: string;
};
const obj: MyType = { [MyConstant]: "hello world" };
console.log(obj[MyConstant]); // "hello world"
在这里,MyConstant 是一个独特的符号,确保没有其他属性可以与它冲突。
TypeScript 允许合并类型声明,这在跨多个文件拆分类型定义或从外部库扩展类型时非常有用。
// file1.ts
interface Point {
x: number;
y: number;
}
// file2.ts
interface Point {
z: number;
}
// Merged Point type: { x: number; y: number; z: number }
通过在 file1.ts 和 file2.ts 中声明 Point,TypeScript 会将两个声明合并为一个类型。 我认为这种行为只发生在 interface 关键字上,而不是 type 关键字,所以我更喜欢 type。
这 10 个 TypeScript 提示和技巧应该可以帮助您提升 TypeScript 技能并编写更强大、灵活和可维护的代码。
不要害怕探索 TypeScript 的高级功能,并在您的项目中利用它们来实现更干净、更安全的代码。