TypeScript作为JavaScript的超集,其最核心的优势在于强大的静态类型系统。通过类型系统,我们可以在编译阶段就发现潜在的错误,提高代码的可维护性和开发效率。本文将从基础到高级,全面解析TypeScript的数据类型系统,帮助你彻底掌握这一核心概念。

一、基础类型:构建类型系统的基石

基础类型是TypeScript类型系统的最小单元,理解它们是掌握整个类型系统的关键。

1. 原始类型

TypeScript为JavaScript的原始类型提供了对应的类型注解:

// 布尔类型 let isDone: boolean = false; // 数字类型 let count: number = 42; let hex: number = 0xF00D; // 支持十六进制 let binary: number = 0b1010; // 支持二进制 // 字符串类型 let name: string = "Alice"; let greeting: string = `Hello, ${name}!`; // 模板字符串 // 空值类型 let u: undefined = undefined; let n: null = null; // Symbol类型 (ES6) let sym: symbol = Symbol("key"); // BigInt类型 (ES2020) let big: bigint = 100n; 

实际应用场景:在函数参数中使用基础类型可以明确约束输入,避免类型错误:

function createGreeting(name: string, times: number): string { return `${name} `.repeat(times).trim(); } // 正确调用 createGreeting("Hello", 3); // "Hello Hello Hello" // 编译错误:Argument of type 'number' is not assignable to parameter of type 'string' // createGreeting(123, 3); 

2. 数组类型

TypeScript提供了两种定义数组类型的方式:

// 方式一:元素类型后加[] let list: number[] = [1, 2, 3]; // 方式二:使用泛型 Array<类型> let list2: Array<number> = [1, 2, 3]; // 多维数组 let matrix: number[][] = [[1, 2], [3, 4]]; // 元组类型(固定长度和类型的数组) let tuple: [string, number] = ["hello", 10]; // tuple = [10, "hello"]; // 错误:类型不匹配 // tuple.push("extra"); // 允许添加,但可能导致运行时错误 

实际应用场景:元组在处理CSV解析或函数返回多个值时特别有用:

// 解析CSV行 function parseCSVLine(line: string): [string, number, string] { const parts = line.split(','); return [parts[0], parseInt(parts[1]), parts[2]]; } const [name, age, city] = parseCSVLine("Alice,25,New York"); console.log(`${name} is ${age} years old, lives in ${city}`); 

3. 枚举类型

枚举为一组相关的数值提供有意义的名称:

// 数字枚举 enum Direction { Up = 1, Down, Left, Right } // Down = 2, Left = 3, Right = 4 // 字符串枚举 enum LogLevel { ERROR = "ERROR", WARN = "WARN", INFO = "INFO", DEBUG = "DEBUG" } // 异构枚举(不推荐) enum Mixed { NO = 0, YES = "YES" } // 常量枚举(编译时会被内联,性能更好) const enum ConstEnum { A = 1, B = 2 } // 编译后:const enum相关代码会被移除,直接替换为字面量 

实际应用场景:枚举在状态管理和错误处理中非常实用:

enum HttpStatus { OK = 200, CREATED = 201, BAD_REQUEST = 400, UNAUTHORIZED = 401, NOT_FOUND = 404, SERVER_ERROR = 500 } function handleResponse(status: HttpStatus): string { switch (status) { case HttpStatus.OK: return "请求成功"; case HttpStatus.NOT_FOUND: return "资源未找到"; case HttpStatus.SERVER_ERROR: return "服务器错误"; default: return "未知状态"; } } console.log(handleResponse(HttpStatus.NOT_FOUND)); // "资源未找到" 

4. Any 和 Unknown

这两个类型用于处理动态类型场景:

// Any: 最宽松的类型,可以进行任何操作(不推荐滥用) let value: any = "hello"; value = 10; // OK value = true; // OK value.nonExistentMethod(); // 编译通过,运行时可能出错 // Unknown: 类型安全的any,必须进行类型检查才能操作 let unknownValue: unknown = "hello"; // unknownValue.toUpperCase(); // 错误:不能直接调用方法 if (typeof unknownValue === "string") { console.log(unknownValue.toUpperCase()); // OK } // 类型断言(两种语法) let someValue: unknown = "this is a string"; let strLength: number = (someValue as string).length; let strLength2: number = (<string>someValue).length; 

实际应用场景:处理第三方库或遗留代码时:

// 处理JSON解析 function parseJSON<T>(jsonString: string): T { try { return JSON.parse(jsonString) as T; } catch (error) { throw new Error("Invalid JSON"); } } interface User { id: number; name: string; } const user = parseJSON<User>('{"id": 1, "name": "Alice"}'); console.log(user.name); // 类型安全 

5. Void 和 Never

// Void: 表示没有返回值的函数 function logMessage(message: string): void { console.log(message); // return; // 可以显式返回void // return "hello"; // 错误:不能返回值 } // Never: 表示永远无法到达的代码点 function error(message: string): never { throw new Error(message); } function infiniteLoop(): never { while (true) { // 永远循环 } } // Never的实用性:穷举检查 type Action = 'create' | 'update' | 'delete'; function handleAction(action: Action): string { switch (action) { case 'create': return "创建资源"; case 'update': return "更新资源"; case 'delete': return "删除资源"; default: // 如果添加了新的Action类型,这里会编译错误 const _exhaustiveCheck: never = action; return _exhaustiveCheck; } } 

二、高级类型:解决复杂场景的利器

高级类型让我们能够创建更复杂、更灵活的类型定义,解决实际开发中的痛点。

1. 联合类型与交叉类型

// 联合类型:表示可能是多种类型之一 function padLeft(value: string, padding: string | number): string { if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; } return padding + value; } // 交叉类型:将多个类型合并为一个 interface Person { name: string; age: number; } interface Employee { employeeId: number; department: string; } type Staff = Person & Employee; const staff: Staff = { name: "Alice", age: 30, employeeId: 1001, department: "Engineering" }; 

实际应用场景:处理API响应的多种可能格式:

type ApiResponse<T> = | { status: 'success'; data: T } | { status: 'error'; code: number; message: string }; function handleResponse(response: ApiResponse<string>) { if (response.status === 'success') { console.log(response.data); // 类型收窄为 string } else { console.error(`Error ${response.code}: ${response.message}`); } } 

2. 类型收窄(Type Narrowing)

TypeScript通过类型收窄来推断更具体的类型:

// typeof 收窄 function padLeft2(value: string, padding: string | number): string { if (typeof padding === "number") { // 这里 padding 被收窄为 number return Array(padding + 1).join(" ") + value; } // 这里 padding 被收窄为 string return padding + value; } // instanceof 收窄 class Dog { bark() { return "Woof!"; } } class Cat { meow() { return "Meow!"; } } function speak(pet: Dog | Cat) { if (pet instanceof Dog) { return pet.bark(); // pet 被收窄为 Dog } return pet.meow(); // pet 被收窄为 Cat } // 自定义类型收窄(类型保护) type Fish = { swim: () => void }; type Bird = { fly: () => void }; function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; } function move(pet: Fish | Bird) { if (isFish(pet)) { pet.swim(); // pet 被收窄为 Fish } else { pet.fly(); // pet 被收窄为 Bird } } 

3. 类型操作符

3.1 keyof 操作符

interface Person { name: string; age: number; location: string; } // keyof Person 的类型是 "name" | "age" | "location" type PersonKeys = keyof Person; // 实用函数:安全地访问对象属性 function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const person = { name: "Alice", age: 30 }; const name = getProperty(person, "name"); // string const age = getProperty(person, "age"); // number // const invalid = getProperty(person, "height"); // 编译错误 

3.2 typeof 操作符

// 获取变量的类型 const point = { x: 10, y: 20 }; type Point = typeof point; // { x: number; y: number } // 在函数中使用 function createPoint(x: number, y: number): typeof point { return { x, y }; } 

3.3 条件类型

// 基本条件类型 type IsString<T> = T extends string ? true : false; type A = IsString<string>; // true type B = IsString<number>; // false // 分布式条件类型 type Flatten<T> = T extends any[] ? T[number] : T; type C = Flatten<[1, 2, 3]>; // 1 | 2 | 3 type D = Flatten<string>; // string // 实用条件类型 type NonNullable<T> = T extends null | undefined ? never : T; type E = NonNullable<string | null | undefined>; // string 

4. 映射类型

映射类型可以从一个类型创建另一个类型:

// 基础映射类型 type Readonly<T> = { readonly [P in keyof T]: T[P]; }; type Partial<T> = { [P in keyof T]?: T[P]; }; type Required<T> = { [P in keyof T]-?: T[P]; }; type Pick<T, K extends keyof T> = { [P in K]: T[P]; }; type Record<K extends string | number | symbol, T> = { [P in K]: T; }; // 使用示例 interface Todo { title: string; description: string; completed: boolean; } type ReadonlyTodo = Readonly<Todo>; // { readonly title: string; readonly description: string; readonly completed: boolean; } type PartialTodo = Partial<Todo>; // { title?: string; description?: string; completed?: boolean; } type TodoPreview = Pick<Todo, "title" | "completed">; // { title: string; completed: boolean; } type TodoMap = Record<string, Todo>; // { [key: string]: Todo } 

实际应用场景:构建灵活的API:

// 创建一个函数,可以部分更新对象 function updateObject<T extends object>( obj: T, updates: Partial<T> ): T { return { ...obj, ...updates }; } const user = { id: 1, name: "Alice", email: "alice@example.com" }; const updated = updateObject(user, { name: "Bob" }); // { id: 1, name: "Bob", email: "alice@example.com" } 

5. 工具类型(Utility Types)

TypeScript内置了许多实用的工具类型:

// Awaited: 获取Promise的返回类型 type PromiseType = Promise<string>; type AwaitedType = Awaited<PromiseType>; // string // Extract: 提取联合类型中的子类型 type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a" // Exclude: 排除联合类型中的某些类型 type T1 = Exclude<"a" | "b" | "c", "a" | "f">; // "b" | "c" // Omit: 从类型中排除某些属性 type T2 = Omit<Todo, "description">; // { title: string; completed: boolean; } // Parameters: 获取函数参数类型 function foo(x: number, y: string): void {} type FooParams = Parameters<typeof foo>; // [number, string] // ReturnType: 获取函数返回类型 type FooReturn = ReturnType<typeof foo>; // void // ConstructorParameters: 获取构造函数参数类型 class MyClass { constructor(x: number, y: string) {} } type MyParams = ConstructorParameters<typeof MyClass>; // [number, string] // InstanceType: 获取构造函数实例类型 type MyInstance = InstanceType<typeof MyClass>; // MyClass 

三、高级技巧:解决编码痛点

1. 字面量类型与类型收窄

// 字面量类型 type Direction = "up" | "down" | "left" | "right"; type Numeric = 1 | 2 | 3 | 4 | 5; // 结合联合类型 function move(direction: Direction, steps: Numeric): string { return `Moving ${direction} for ${steps} steps`; } move("up", 3); // OK // move("forward", 2); // 错误 // move("left", 6); // 错误 // 类型收窄的进阶应用 type RequestState = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: string } | { status: 'error'; error: Error }; function renderRequest(state: RequestState): string { switch (state.status) { case 'idle': return "等待开始"; case 'loading': return "加载中..."; case 'success': // 这里 state 被收窄为 { status: 'success'; data: string } return `成功: ${state.data}`; case 'error': // 这里 state 被收窄为 { status: 'error'; error: Error } return `错误: ${state.error.message}`; } } 

2. 模式匹配与类型提取

// 使用条件类型进行模式匹配 type GetPromiseResult<T> = T extends Promise<infer U> ? U : never; type Result = GetPromiseResult<Promise<string[]>>; // string[] // 提取数组元素类型 type GetArrayElementType<T> = T extends (infer U)[] ? U : never; type Element = GetArrayElementType<string[]>; // string // 提取函数返回类型 type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never; type FuncReturn = GetReturnType<() => string>; // string // 实际应用:API响应处理 type ApiResponse = { users: { id: number; name: string }[]; posts: { id: number; title: string }[]; }; type User = GetArrayElementType<ApiResponse['users']>; // { id: number; name: string } 

3. 递归类型

// 树形结构 interface TreeNode<T> { value: T; children?: TreeNode<T>[]; } const tree: TreeNode<string> = { value: "root", children: [ { value: "child1" }, { value: "child2", children: [ { value: "grandchild1" } ] } ] }; // JSON类型(递归引用自身) type JSONValue = | string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue }; const jsonData: JSONValue = { name: "Alice", age: 30, hobbies: ["reading", "coding"], address: { city: "New York", zip: "10001" } }; 

4. 品牌类型(Brand Types)

解决数值混淆问题:

// 品牌类型:防止不同类型之间的数值混淆 type UserId = number & { readonly __brand: "UserId" }; type ProductId = number & { readonly __brand: "ProductId" }; function createUserId(id: number): UserId { return id as UserId; } function createProductId(id: number): ProductId { return id as ProductId; } function getUser(userId: UserId): string { return `User ${userId}`; } function getProduct(productId: ProductId): string { return `Product ${productId}`; } const userId = createUserId(123); const productId = createProductId(456); getUser(userId); // OK getUser(productId); // 编译错误! 

5. 函数重载与泛型

// 函数重载:为同一函数提供多个类型签名 function getData(id: number): Promise<string>; function getData(ids: number[]): Promise<string[]>; function getData(idOrIds: number | number[]): Promise<string | string[]> { if (typeof idOrIds === "number") { return Promise.resolve(`Data for ${idOrIds}`); } else { return Promise.resolve(idOrIds.map(id => `Data for ${id}`)); } } // 使用 async function example() { const single = await getData(1); // string const multiple = await getData([1, 2, 3]); // string[] } // 泛型函数 function identity<T>(arg: T): T { return arg; } const output = identity<string>("hello"); const numOutput = identity<number>(123); // 带约束的泛型 function loggingIdentity<T extends { length: number }>(arg: T): T { console.log(arg.length); return arg; } loggingIdentity("hello"); // OK loggingIdentity([1, 2, 3]); // OK // loggingIdentity(123); // 错误:number没有length属性 

四、实际项目中的最佳实践

1. 严格模式配置

tsconfig.json中启用严格模式:

{ "compilerOptions": { "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true } } 

2. 接口 vs 类型别名

// 接口:适合定义对象形状,支持声明合并 interface User { name: string; } interface User { age: number; } // 合并后:{ name: string; age: number } // 类型别名:更灵活,支持联合、交叉等复杂类型 type UserOrAdmin = { name: string } | { admin: true }; // 选择建议: // - 优先使用接口定义对象形状 // - 使用类型别名定义联合类型、元组等复杂类型 

3. 避免 any,使用 unknown

// ❌ 不推荐 function badParse(json: string): any { return JSON.parse(json); } // ✅ 推荐 function goodParse<T>(json: string): T { return JSON.parse(json) as T; } // ✅ 或者使用 unknown function safeParse(json: string): unknown { return JSON.parse(json); } 

4. 使用 const 断言

// const 断言:使字面量变为不可变类型 const directions = ["up", "down", "left", "right"] as const; // 类型:readonly ["up", "down", "left", "right"] type Direction = typeof directions[number]; // "up" | "down" | "left" | "right" // 对象const断言 const config = { api: "https://api.example.com", timeout: 5000 } as const; // 类型:{ readonly api: "https://api.example.com"; readonly timeout: 5000 } 

5. 使用声明文件管理第三方库

// custom.d.ts declare module "legacy-library" { export function doSomething(x: number): string; export const version: string; } // 使用 import { doSomething } from "legacy-library"; doSomething(42); // 类型安全 

五、总结

TypeScript的类型系统是一个强大而灵活的工具,从基础的原始类型到高级的条件类型和映射类型,每一层都为解决特定的编程问题而设计。掌握这些类型概念可以帮助我们:

  1. 在编译时发现错误:而不是在运行时
  2. 提高代码可读性:类型注解本身就是文档
  3. 增强IDE支持:自动完成、重构、导航
  4. 改善代码维护性:类型系统强制执行契约

记住,类型系统的目的是帮助我们编写更好的代码,而不是增加负担。从基础开始,逐步深入,最终你会发现自己能够用类型系统优雅地解决复杂的编程问题。# TypeScript数据类型详解从基础到高级掌握类型系统核心概念解决编码痛点提升代码质量与开发效率

TypeScript作为JavaScript的超集,其最核心的优势在于强大的静态类型系统。通过类型系统,我们可以在编译阶段就发现潜在的错误,提高代码的可维护性和开发效率。本文将从基础到高级,全面解析TypeScript的数据类型系统,帮助你彻底掌握这一核心概念。

一、基础类型:构建类型系统的基石

基础类型是TypeScript类型系统的最小单元,理解它们是掌握整个类型系统的关键。

1. 原始类型

TypeScript为JavaScript的原始类型提供了对应的类型注解:

// 布尔类型 let isDone: boolean = false; // 数字类型 let count: number = 42; let hex: number = 0xF00D; // 支持十六进制 let binary: number = 0b1010; // 支持二进制 // 字符串类型 let name: string = "Alice"; let greeting: string = `Hello, ${name}!`; // 模板字符串 // 空值类型 let u: undefined = undefined; let n: null = null; // Symbol类型 (ES6) let sym: symbol = Symbol("key"); // BigInt类型 (ES2020) let big: bigint = 100n; 

实际应用场景:在函数参数中使用基础类型可以明确约束输入,避免类型错误:

function createGreeting(name: string, times: number): string { return `${name} `.repeat(times).trim(); } // 正确调用 createGreeting("Hello", 3); // "Hello Hello Hello" // 编译错误:Argument of type 'number' is not assignable to parameter of type 'string' // createGreeting(123, 3); 

2. 数组类型

TypeScript提供了两种定义数组类型的方式:

// 方式一:元素类型后加[] let list: number[] = [1, 2, 3]; // 方式二:使用泛型 Array<类型> let list2: Array<number> = [1, 2, 3]; // 多维数组 let matrix: number[][] = [[1, 2], [3, 4]]; // 元组类型(固定长度和类型的数组) let tuple: [string, number] = ["hello", 10]; // tuple = [10, "hello"]; // 错误:类型不匹配 // tuple.push("extra"); // 允许添加,但可能导致运行时错误 

实际应用场景:元组在处理CSV解析或函数返回多个值时特别有用:

// 解析CSV行 function parseCSVLine(line: string): [string, number, string] { const parts = line.split(','); return [parts[0], parseInt(parts[1]), parts[2]]; } const [name, age, city] = parseCSVLine("Alice,25,New York"); console.log(`${name} is ${age} years old, lives in ${city}`); 

3. 枚举类型

枚举为一组相关的数值提供有意义的名称:

// 数字枚举 enum Direction { Up = 1, Down, Left, Right } // Down = 2, Left = 3, Right = 4 // 字符串枚举 enum LogLevel { ERROR = "ERROR", WARN = "WARN", INFO = "INFO", DEBUG = "DEBUG" } // 异构枚举(不推荐) enum Mixed { NO = 0, YES = "YES" } // 常量枚举(编译时会被内联,性能更好) const enum ConstEnum { A = 1, B = 2 } // 编译后:const enum相关代码会被移除,直接替换为字面量 

实际应用场景:枚举在状态管理和错误处理中非常实用:

enum HttpStatus { OK = 200, CREATED = 201, BAD_REQUEST = 400, UNAUTHORIZED = 401, NOT_FOUND = 404, SERVER_ERROR = 500 } function handleResponse(status: HttpStatus): string { switch (status) { case HttpStatus.OK: return "请求成功"; case HttpStatus.NOT_FOUND: return "资源未找到"; case HttpStatus.SERVER_ERROR: return "服务器错误"; default: return "未知状态"; } } console.log(handleResponse(HttpStatus.NOT_FOUND)); // "资源未找到" 

4. Any 和 Unknown

这两个类型用于处理动态类型场景:

// Any: 最宽松的类型,可以进行任何操作(不推荐滥用) let value: any = "hello"; value = 10; // OK value = true; // OK value.nonExistentMethod(); // 编译通过,运行时可能出错 // Unknown: 类型安全的any,必须进行类型检查才能操作 let unknownValue: unknown = "hello"; // unknownValue.toUpperCase(); // 错误:不能直接调用方法 if (typeof unknownValue === "string") { console.log(unknownValue.toUpperCase()); // OK } // 类型断言(两种语法) let someValue: unknown = "this is a string"; let strLength: number = (someValue as string).length; let strLength2: number = (<string>someValue).length; 

实际应用场景:处理第三方库或遗留代码时:

// 处理JSON解析 function parseJSON<T>(jsonString: string): T { try { return JSON.parse(jsonString) as T; } catch (error) { throw new Error("Invalid JSON"); } } interface User { id: number; name: string; } const user = parseJSON<User>('{"id": 1, "name": "Alice"}'); console.log(user.name); // 类型安全 

5. Void 和 Never

// Void: 表示没有返回值的函数 function logMessage(message: string): void { console.log(message); // return; // 可以显式返回void // return "hello"; // 错误:不能返回值 } // Never: 表示永远无法到达的代码点 function error(message: string): never { throw new Error(message); } function infiniteLoop(): never { while (true) { // 永远循环 } } // Never的实用性:穷举检查 type Action = 'create' | 'update' | 'delete'; function handleAction(action: Action): string { switch (action) { case 'create': return "创建资源"; case 'update': return "更新资源"; case 'delete': return "删除资源"; default: // 如果添加了新的Action类型,这里会编译错误 const _exhaustiveCheck: never = action; return _exhaustiveCheck; } } 

二、高级类型:解决复杂场景的利器

高级类型让我们能够创建更复杂、更灵活的类型定义,解决实际开发中的痛点。

1. 联合类型与交叉类型

// 联合类型:表示可能是多种类型之一 function padLeft(value: string, padding: string | number): string { if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; } return padding + value; } // 交叉类型:将多个类型合并为一个 interface Person { name: string; age: number; } interface Employee { employeeId: number; department: string; } type Staff = Person & Employee; const staff: Staff = { name: "Alice", age: 30, employeeId: 1001, department: "Engineering" }; 

实际应用场景:处理API响应的多种可能格式:

type ApiResponse<T> = | { status: 'success'; data: T } | { status: 'error'; code: number; message: string }; function handleResponse(response: ApiResponse<string>) { if (response.status === 'success') { console.log(response.data); // 类型收窄为 string } else { console.error(`Error ${response.code}: ${response.message}`); } } 

2. 类型收窄(Type Narrowing)

TypeScript通过类型收窄来推断更具体的类型:

// typeof 收窄 function padLeft2(value: string, padding: string | number): string { if (typeof padding === "number") { // 这里 padding 被收窄为 number return Array(padding + 1).join(" ") + value; } // 这里 padding 被收窄为 string return padding + value; } // instanceof 收窄 class Dog { bark() { return "Woof!"; } } class Cat { meow() { return "Meow!"; } } function speak(pet: Dog | Cat) { if (pet instanceof Dog) { return pet.bark(); // pet 被收窄为 Dog } return pet.meow(); // pet 被收窄为 Cat } // 自定义类型收窄(类型保护) type Fish = { swim: () => void }; type Bird = { fly: () => void }; function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; } function move(pet: Fish | Bird) { if (isFish(pet)) { pet.swim(); // pet 被收窄为 Fish } else { pet.fly(); // pet 被收窄为 Bird } } 

3. 类型操作符

3.1 keyof 操作符

interface Person { name: string; age: number; location: string; } // keyof Person 的类型是 "name" | "age" | "location" type PersonKeys = keyof Person; // 实用函数:安全地访问对象属性 function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const person = { name: "Alice", age: 30 }; const name = getProperty(person, "name"); // string const age = getProperty(person, "age"); // number // const invalid = getProperty(person, "height"); // 编译错误 

3.2 typeof 操作符

// 获取变量的类型 const point = { x: 10, y: 20 }; type Point = typeof point; // { x: number; y: number } // 在函数中使用 function createPoint(x: number, y: number): typeof point { return { x, y }; } 

3.3 条件类型

// 基本条件类型 type IsString<T> = T extends string ? true : false; type A = IsString<string>; // true type B = IsString<number>; // false // 分布式条件类型 type Flatten<T> = T extends any[] ? T[number] : T; type C = Flatten<[1, 2, 3]>; // 1 | 2 | 3 type D = Flatten<string>; // string // 实用条件类型 type NonNullable<T> = T extends null | undefined ? never : T; type E = NonNullable<string | null | undefined>; // string 

4. 映射类型

映射类型可以从一个类型创建另一个类型:

// 基础映射类型 type Readonly<T> = { readonly [P in keyof T]: T[P]; }; type Partial<T> = { [P in keyof T]?: T[P]; }; type Required<T> = { [P in keyof T]-?: T[P]; }; type Pick<T, K extends keyof T> = { [P in K]: T[P]; }; type Record<K extends string | number | symbol, T> = { [P in K]: T; }; // 使用示例 interface Todo { title: string; description: string; completed: boolean; } type ReadonlyTodo = Readonly<Todo>; // { readonly title: string; readonly description: string; readonly completed: boolean; } type PartialTodo = Partial<Todo>; // { title?: string; description?: string; completed?: boolean; } type TodoPreview = Pick<Todo, "title" | "completed">; // { title: string; completed: boolean; } type TodoMap = Record<string, Todo>; // { [key: string]: Todo } 

实际应用场景:构建灵活的API:

// 创建一个函数,可以部分更新对象 function updateObject<T extends object>( obj: T, updates: Partial<T> ): T { return { ...obj, ...updates }; } const user = { id: 1, name: "Alice", email: "alice@example.com" }; const updated = updateObject(user, { name: "Bob" }); // { id: 1, name: "Bob", email: "alice@example.com" } 

5. 工具类型(Utility Types)

TypeScript内置了许多实用的工具类型:

// Awaited: 获取Promise的返回类型 type PromiseType = Promise<string>; type AwaitedType = Awaited<PromiseType>; // string // Extract: 提取联合类型中的子类型 type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a" // Exclude: 排除联合类型中的某些类型 type T1 = Exclude<"a" | "b" | "c", "a" | "f">; // "b" | "c" // Omit: 从类型中排除某些属性 type T2 = Omit<Todo, "description">; // { title: string; completed: boolean; } // Parameters: 获取函数参数类型 function foo(x: number, y: string): void {} type FooParams = Parameters<typeof foo>; // [number, string] // ReturnType: 获取函数返回类型 type FooReturn = ReturnType<typeof foo>; // void // ConstructorParameters: 获取构造函数参数类型 class MyClass { constructor(x: number, y: string) {} } type MyParams = ConstructorParameters<typeof MyClass>; // [number, string] // InstanceType: 获取构造函数实例类型 type MyInstance = InstanceType<typeof MyClass>; // MyClass 

三、高级技巧:解决编码痛点

1. 字面量类型与类型收窄

// 字面量类型 type Direction = "up" | "down" | "left" | "right"; type Numeric = 1 | 2 | 3 | 4 | 5; // 结合联合类型 function move(direction: Direction, steps: Numeric): string { return `Moving ${direction} for ${steps} steps`; } move("up", 3); // OK // move("forward", 2); // 错误 // move("left", 6); // 错误 // 类型收窄的进阶应用 type RequestState = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: string } | { status: 'error'; error: Error }; function renderRequest(state: RequestState): string { switch (state.status) { case 'idle': return "等待开始"; case 'loading': return "加载中..."; case 'success': // 这里 state 被收窄为 { status: 'success'; data: string } return `成功: ${state.data}`; case 'error': // 这里 state 被收窄为 { status: 'error'; error: Error } return `错误: ${state.error.message}`; } } 

2. 模式匹配与类型提取

// 使用条件类型进行模式匹配 type GetPromiseResult<T> = T extends Promise<infer U> ? U : never; type Result = GetPromiseResult<Promise<string[]>>; // string[] // 提取数组元素类型 type GetArrayElementType<T> = T extends (infer U)[] ? U : never; type Element = GetArrayElementType<string[]>; // string // 提取函数返回类型 type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never; type FuncReturn = GetReturnType<() => string>; // string // 实际应用:API响应处理 type ApiResponse = { users: { id: number; name: string }[]; posts: { id: number; title: string }[]; }; type User = GetArrayElementType<ApiResponse['users']>; // { id: number; name: string } 

3. 递归类型

// 树形结构 interface TreeNode<T> { value: T; children?: TreeNode<T>[]; } const tree: TreeNode<string> = { value: "root", children: [ { value: "child1" }, { value: "child2", children: [ { value: "grandchild1" } ] } ] }; // JSON类型(递归引用自身) type JSONValue = | string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue }; const jsonData: JSONValue = { name: "Alice", age: 30, hobbies: ["reading", "coding"], address: { city: "New York", zip: "10001" } }; 

4. 品牌类型(Brand Types)

解决数值混淆问题:

// 品牌类型:防止不同类型之间的数值混淆 type UserId = number & { readonly __brand: "UserId" }; type ProductId = number & { readonly __brand: "ProductId" }; function createUserId(id: number): UserId { return id as UserId; } function createProductId(id: number): ProductId { return id as ProductId; } function getUser(userId: UserId): string { return `User ${userId}`; } function getProduct(productId: ProductId): string { return `Product ${productId}`; } const userId = createUserId(123); const productId = createProductId(456); getUser(userId); // OK getUser(productId); // 编译错误! 

5. 函数重载与泛型

// 函数重载:为同一函数提供多个类型签名 function getData(id: number): Promise<string>; function getData(ids: number[]): Promise<string[]>; function getData(idOrIds: number | number[]): Promise<string | string[]> { if (typeof idOrIds === "number") { return Promise.resolve(`Data for ${idOrIds}`); } else { return Promise.resolve(idOrIds.map(id => `Data for ${id}`)); } } // 使用 async function example() { const single = await getData(1); // string const multiple = await getData([1, 2, 3]); // string[] } // 泛型函数 function identity<T>(arg: T): T { return arg; } const output = identity<string>("hello"); const numOutput = identity<number>(123); // 带约束的泛型 function loggingIdentity<T extends { length: number }>(arg: T): T { console.log(arg.length); return arg; } loggingIdentity("hello"); // OK loggingIdentity([1, 2, 3]); // OK // loggingIdentity(123); // 错误:number没有length属性 

四、实际项目中的最佳实践

1. 严格模式配置

tsconfig.json中启用严格模式:

{ "compilerOptions": { "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true } } 

2. 接口 vs 类型别名

// 接口:适合定义对象形状,支持声明合并 interface User { name: string; } interface User { age: number; } // 合并后:{ name: string; age: number } // 类型别名:更灵活,支持联合、交叉等复杂类型 type UserOrAdmin = { name: string } | { admin: true }; // 选择建议: // - 优先使用接口定义对象形状 // - 使用类型别名定义联合类型、元组等复杂类型 

3. 避免 any,使用 unknown

// ❌ 不推荐 function badParse(json: string): any { return JSON.parse(json); } // ✅ 推荐 function goodParse<T>(json: string): T { return JSON.parse(json) as T; } // ✅ 或者使用 unknown function safeParse(json: string): unknown { return JSON.parse(json); } 

4. 使用 const 断言

// const 断言:使字面量变为不可变类型 const directions = ["up", "down", "left", "right"] as const; // 类型:readonly ["up", "down", "left", "right"] type Direction = typeof directions[number]; // "up" | "down" | "left" | "right" // 对象const断言 const config = { api: "https://api.example.com", timeout: 5000 } as const; // 类型:{ readonly api: "https://api.example.com"; readonly timeout: 5000 } 

5. 使用声明文件管理第三方库

// custom.d.ts declare module "legacy-library" { export function doSomething(x: number): string; export const version: string; } // 使用 import { doSomething } from "legacy-library"; doSomething(42); // 类型安全 

五、总结

TypeScript的类型系统是一个强大而灵活的工具,从基础的原始类型到高级的条件类型和映射类型,每一层都为解决特定的编程问题而设计。掌握这些类型概念可以帮助我们:

  1. 在编译时发现错误:而不是在运行时
  2. 提高代码可读性:类型注解本身就是文档
  3. 增强IDE支持:自动完成、重构、导航
  4. 改善代码维护性:类型系统强制执行契约

记住,类型系统的目的是帮助我们编写更好的代码,而不是增加负担。从基础开始,逐步深入,最终你会发现自己能够用类型系统优雅地解决复杂的编程问题。