TypeScript 作为 JavaScript 的超集,其核心优势之一就是强大的静态类型系统。理解 TypeScript 的数据类型是掌握这门语言的关键。本文将从基础类型开始,逐步深入到高级类型和实战技巧,并解答常见问题,帮助你全面掌握 TypeScript 的类型系统。

1. 基础类型 (Primitive Types)

TypeScript 的基础类型直接对应 JavaScript 的原始值类型,但提供了静态类型检查。

1.1 基本类型

// 字符串类型 let message: string = "Hello, TypeScript!"; let name: string = 'Alice'; // 数字类型 let count: number = 42; let price: number = 99.99; // 布尔类型 let isDone: boolean = false; let isActive: boolean = true; // 任意类型 (any) - 应尽量避免使用 let notSure: any = 4; notSure = "maybe a string instead"; notSure = false; // 不会报错,但失去了类型安全 // 未知类型 (unknown) - 比 any 更安全 let unknownValue: unknown = "hello"; // unknownValue.toUpperCase(); // 错误!需要先进行类型检查 if (typeof unknownValue === "string") { console.log(unknownValue.toUpperCase()); // 正确 } // 空值类型 (null 和 undefined) let nullValue: null = null; let undefinedValue: undefined = undefined; // Void 类型 - 通常用于函数没有返回值的情况 function warnUser(): void { console.log("This is a warning message"); } // Never 类型 - 表示永远不会到达的代码 function error(message: string): never { throw new Error(message); } function fail() { return error("Something failed"); } // Symbol 类型 (ES6) let sym1: symbol = Symbol("key"); let sym2: symbol = Symbol.for("key"); // BigInt 类型 (ES2020) let bigNumber: bigint = 9007199254740991n; let anotherBig: bigint = 100n; 

1.2 数组类型

// 数组类型声明方式1:类型[] let numbers: number[] = [1, 2, 3, 4, 5]; let names: string[] = ["Alice", "Bob", "Charlie"]; // 数组类型声明方式2:Array<类型> let numbers2: Array<number> = [1, 2, 3]; let names2: Array<string> = ["Alice", "Bob"]; // 混合类型数组 let mixed: (string | number)[] = [1, "two", 3, "four"]; 

1.3 元组类型 (Tuple)

元组是固定长度和类型的数组。

// 基本元组 let tuple: [string, number] = ["hello", 10]; // 可选元组元素 let optionalTuple: [string, number?] = ["hello"]; optionalTuple = ["hello", 20]; // 命名元组 (TypeScript 4.0+) let namedTuple: [name: string, age: number] = ["Alice", 25]; // 元组的使用示例 function parseCoordinate(coord: [number, number]): { x: number; y: number } { return { x: coord[0], y: coord[1] }; } const point = parseCoordinate([10, 20]); console.log(point.x, point.y); // 10 20 

1.4 对象类型

// 对象类型声明 let person: { name: string; age: number; email?: string; // 可选属性 } = { name: "Alice", age: 25, email: "alice@example.com" }; // 使用接口声明对象类型 interface User { id: number; username: string; email: string; isActive: boolean; } let user: User = { id: 1, username: "alice", email: "alice@example.com", isActive: true }; // 使用类型别名 type Point = { x: number; y: number; }; let point: Point = { x: 10, y: 20 }; 

2. 高级类型 (Advanced Types)

2.1 联合类型 (Union Types)

// 基本联合类型 let id: string | number = "123"; id = 456; // 也可以是数字 // 函数参数联合类型 function processId(id: string | number) { if (typeof id === "string") { console.log(id.toUpperCase()); } else { console.log(id.toFixed(2)); } } // 联合类型与数组 let ids: (string | number)[] = [1, "two", 3, "four"]; 

2.2 交叉类型 (Intersection Types)

// 基本交叉类型 type Named = { name: string }; type Aged = { age: number }; type NamedAndAged = Named & Aged; let person: NamedAndAged = { name: "Alice", age: 25 }; // 接口交叉 interface Employee { employeeId: number; department: string; } interface Manager { teamSize: number; budget: number; } type TeamLead = Employee & Manager; let lead: TeamLead = { employeeId: 1, department: "Engineering", teamSize: 5, budget: 100000 }; 

2.3 类型别名 (Type Aliases)

// 基本类型别名 type UserID = string | number; type UserStatus = "active" | "inactive" | "pending"; // 复杂类型别名 type User = { id: UserID; username: string; status: UserStatus; roles: string[]; }; // 类型别名与接口的区别 // 接口可以被继承和声明合并,类型别名不能 interface Config { apiUrl: string; timeout: number; } type Settings = { apiUrl: string; timeout: number; }; // 接口可以声明合并 interface Config { retries: number; } // 类型别名不能声明合并 // type Settings = { retries: number }; // 错误 

2.4 字面量类型 (Literal Types)

// 字符串字面量 type Direction = "up" | "down" | "left" | "right"; function move(direction: Direction) { console.log(`Moving ${direction}`); } move("up"); // 正确 // move("forward"); // 错误 // 数字字面量 type HTTPStatus = 200 | 201 | 400 | 404 | 500; function handleResponse(status: HTTPStatus) { if (status === 200) { console.log("Success"); } } // 布尔字面量 type Enabled = true | false; let flag: Enabled = true; // 模板字面量类型 (TypeScript 4.1+) type EventName = "click" | "scroll"; type HandlerName = `on${EventName}`; // "onclick" | "onscroll" function addHandler(name: HandlerName) { console.log(`Adding handler: ${name}`); } addHandler("onclick"); // 正确 

2.5 可辨识联合 (Discriminated Unions)

// 可辨识联合类型 type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number } | { kind: "rectangle"; width: number; height: number }; function getArea(shape: Shape): number { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "square": return shape.side ** 2; case "rectangle": return shape.width * shape.height; default: // TypeScript 知道这里不可能有其他情况 const _exhaustiveCheck: never = shape; return 0; } } // 使用示例 const circle: Shape = { kind: "circle", radius: 5 }; const square: Shape = { kind: "square", side: 4 }; console.log(getArea(circle)); // 78.53981633974483 console.log(getArea(square)); // 16 

2.6 索引签名 (Index Signatures)

// 索引签名类型 interface StringDictionary { [key: string]: string; } const dict: StringDictionary = { name: "Alice", email: "alice@example.com", country: "USA" }; // 数字索引签名 interface NumberArray { [index: number]: string; } const arr: NumberArray = ["one", "two", "three"]; console.log(arr[0]); // "one" // 混合索引签名 interface MixedDictionary { [key: string]: any; id: number; // 明确声明的属性必须符合索引签名的类型 } const mixed: MixedDictionary = { id: 1, name: "Alice", age: 25 }; 

2.7 映射类型 (Mapped Types)

// 基础映射类型 type Readonly<T> = { readonly [P in keyof T]: T[P]; }; type Optional<T> = { [P in keyof T]?: T[P]; }; // 使用示例 interface User { name: string; age: number; email: string; } type ReadonlyUser = Readonly<User>; type OptionalUser = Optional<User>; const readonlyUser: ReadonlyUser = { name: "Alice", // age: 25, // 错误:age 是只读的 email: "alice@example.com" }; // 内置映射类型 type PartialUser = Partial<User>; // 所有属性可选 type RequiredUser = Required<User>; // 所有属性必选 type PickUser = Pick<User, "name" | "email">; // 选择特定属性 type RecordUser = Record<"admin" | "user", User>; // 创建键值对类型 // 高级映射类型 type Getters<T> = { [P in keyof T as `get${Capitalize<string & P>}`]: () => T[P]; }; type UserGetters = Getters<User>; // 等价于: // { // getName: () => string; // getAge: () => number; // getEmail: () => string; // } 

2.8 条件类型 (Conditional Types)

// 基础条件类型 type IsString<T> = T extends string ? "yes" : "no"; type Test1 = IsString<string>; // "yes" type Test2 = IsString<number>; // "no" // 分布式条件类型 type Flatten<T> = T extends any[] ? T[number] : T; type Flattened = Flatten<[1, 2, 3]>; // number type NotFlattened = Flatten<number>; // number // infer 关键字 type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never; function getString(): string { return "hello"; } function getNumber(): number { return 42; } type StringReturn = ReturnType<typeof getString>; // string type NumberReturn = ReturnType<typeof getNumber>; // number // 实战:从对象类型中提取值类型 type ValueOf<T> = T[keyof T]; type UserValues = ValueOf<User>; // string | number 

2.9 模板字面量类型 (Template Literal Types)

// 基础模板字面量类型 type EventName = "click" | "scroll" | "keydown"; type HandlerName = `on${EventName}`; // "onclick" | "onscroll" | "onkeydown" // 复杂模板字面量类型 type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE"; type Route = `/api/${HTTPMethod}/${string}`; // "/api/GET/users" 等 function handleRoute(route: Route) { console.log(`Handling route: ${route}`); } handleRoute("/api/GET/users"); // 正确 handleRoute("/api/POST/items"); // 正确 // handleRoute("/api/GET"); // 错误:缺少路径部分 // 实战:生成 CSS 类名 type Color = "red" | "blue" | "green"; type Size = "sm" | "md" | "lg"; type ClassName = `text-${Color}` | `bg-${Color}` | `p-${Size}`; function applyClass(className: ClassName) { console.log(`Applying class: ${className}`); } applyClass("text-red"); // 正确 applyClass("bg-blue"); // 正确 applyClass("p-lg"); // 正确 

3. 实战技巧与最佳实践

3.1 类型守卫 (Type Guards)

// typeof 类型守卫 function isString(value: unknown): value is string { return typeof value === "string"; } function isNumber(value: unknown): value is number { return typeof value === "number"; } // in 类型守卫 interface Fish { swim(): void; } interface Bird { fly(): void; } function move(pet: Fish | Bird) { if ("swim" in pet) { pet.swim(); // TypeScript 知道 pet 是 Fish } else { pet.fly(); // TypeScript 知道 pet 是 Bird } } // instanceof 类型守卫 class Dog { bark() { console.log("Woof!"); } } class Cat { meow() { console.log("Meow!"); } } function makeSound(animal: Dog | Cat) { if (animal instanceof Dog) { animal.bark(); // TypeScript 知道是 Dog } else { animal.meow(); // TypeScript 知道是 Cat } } // 自定义类型守卫 interface User { id: number; username: string; email: string; isAdmin: boolean; } function isAdmin(user: User): user is User & { isAdmin: true } { return user.isAdmin === true; } function handleUser(user: User) { if (isAdmin(user)) { // user 现在被收窄为 User & { isAdmin: true } console.log(`Admin user: ${user.username}`); } else { console.log(`Regular user: ${user.username}`); } } 

3.2 类型收窄 (Type Narrowing)

// 使用控制流分析 function processValue(value: string | number | null) { if (value === null) { // value 是 null return; } if (typeof value === "string") { // value 是 string console.log(value.toUpperCase()); } else { // value 是 number console.log(value.toFixed(2)); } } // 使用非空断言 function getName(user: { name?: string } | null) { // 非空断言:告诉 TypeScript 这个值不会是 null/undefined return user!.name!; } // 使用可选链操作符 function getEmail(user: { email?: string } | null) { return user?.email; // 如果 user 是 null/undefined,返回 undefined } // 使用空值合并操作符 function getDisplayName(user: { name?: string } | null) { return user?.name ?? "Anonymous"; } 

3.3 泛型 (Generics)

// 基础泛型函数 function identity<T>(arg: T): T { return arg; } let output1 = identity<string>("hello"); let output2 = identity<number>(42); // 泛型接口 interface Box<T> { value: T; } let stringBox: Box<string> = { value: "hello" }; let numberBox: Box<number> = { value: 42 }; // 泛型类 class GenericNumber<T> { zeroValue: T; add: (x: T, y: T) => T; } let myGenericNumber = new GenericNumber<number>(); myGenericNumber.zeroValue = 0; myGenericNumber.add = (x, y) => x + y; // 泛型约束 function loggingIdentity<T extends { length: number }>(arg: T): T { console.log(arg.length); return arg; } loggingIdentity("hello"); // 正确 loggingIdentity([1, 2, 3]); // 正确 // loggingIdentity(42); // 错误:number 没有 length 属性 // 泛型与默认类型 function createArray<T = string>(length: number, value: T): T[] { return Array(length).fill(value); } let stringArray = createArray(3, "hello"); // string[] let numberArray = createArray(3, 42); // number[] let defaultArray = createArray(3, "default"); // string[] // 泛型在 React 组件中的应用 import React from 'react'; interface Props<T> { data: T[]; renderItem: (item: T) => React.ReactNode; } function List<T>({ data, renderItem }: Props<T>) { return <div>{data.map(renderItem)}</div>; } // 使用示例 <List<string> data={["apple", "banana", "cherry"]} renderItem={(item) => <span>{item}</span>} /> 

3.4 类型推断与类型断言

// 类型推断 let x = 3; // TypeScript 推断为 number let y = "hello"; // TypeScript 推断为 string function add(a: number, b: number) { return a + b; // TypeScript 推断返回类型为 number } // 类型断言 let someValue: unknown = "this is a string"; let strLength: number = (someValue as string).length; // 另一种语法 let strLength2: number = (<string>someValue).length; // 非空断言 function getNameLength(name: string | null) { return name!.length; // 假设 name 不是 null } // const 断言 const names = ["Alice", "Bob", "Charlie"] as const; // names 现在是 readonly ["Alice", "Bob", "Charlie"] // names.push("David"); // 错误:只读数组 const config = { apiUrl: "https://api.example.com", timeout: 5000, } as const; // config 现在是只读的,且属性类型是字面量类型 

3.5 类型兼容性

// 结构类型系统 interface Point { x: number; y: number; } interface Point3D { x: number; y: number; z: number; } let point2D: Point = { x: 1, y: 2 }; let point3D: Point3D = { x: 1, y: 2, z: 3 }; // Point3D 可以赋值给 Point,因为它有 x 和 y point2D = point3D; // 正确 // 函数类型兼容性 let f1: (a: number) => number = (a) => a; let f2: (a: number, b: number) => number = (a, b) => a + b; // f1 = f2; // 错误:参数数量不匹配 f2 = f1; // 正确:可以忽略多余参数 // 返回值兼容性 let getNumber: () => number = () => 42; let getAny: () => any = () => "hello"; getNumber = getAny; // 错误:any 不是 number 的子类型 getAny = getNumber; // 正确:number 是 any 的子类型 

3.6 类型声明文件 (.d.ts)

// 声明文件示例:my-library.d.ts declare module "my-library" { export function hello(name: string): string; export const version: string; export interface Config { apiUrl: string; timeout: number; } } // 使用声明文件 import { hello, version, Config } from "my-library"; console.log(hello("World")); console.log(version); const config: Config = { apiUrl: "https://api.example.com", timeout: 5000, }; // 全局声明 declare global { interface Window { myGlobalFunction: () => void; } } // 在代码中使用 window.myGlobalFunction = () => { console.log("Global function called"); }; 

4. 常见问题解答

4.1 为什么我应该使用 TypeScript 而不是 JavaScript?

回答: TypeScript 提供了静态类型检查,可以在编译时捕获错误,而不是在运行时。这有助于:

  1. 早期错误检测:在代码运行前发现类型错误
  2. 更好的开发体验:IDE 提供更好的自动补全和重构支持
  3. 代码可维护性:类型文档化了代码的意图
  4. 团队协作:类型定义使代码更易于理解和维护

4.2 如何处理第三方库没有类型定义的情况?

回答: 有几种方法:

  1. 安装 @types 包:大多数流行库都有社区维护的类型定义
     npm install --save-dev @types/lodash 
  2. 创建自定义声明文件:创建 .d.ts 文件
     // custom.d.ts declare module "untyped-library" { export function doSomething(arg: string): void; } 
  3. 使用 any 类型:作为临时解决方案
     const library: any = require("untyped-library"); 
  4. 使用 unknown 类型:比 any 更安全
     const library: unknown = require("untyped-library"); 

4.3 如何处理复杂的嵌套对象类型?

回答: 使用类型别名和接口组合:

// 定义基础类型 type UserID = string; type UserRole = "admin" | "user" | "guest"; // 定义嵌套对象类型 interface UserProfile { id: UserID; name: string; email: string; preferences: { theme: "light" | "dark"; notifications: boolean; language: string; }; roles: UserRole[]; metadata: { createdAt: Date; updatedAt: Date; lastLogin?: Date; }; } // 使用示例 const user: UserProfile = { id: "123", name: "Alice", email: "alice@example.com", preferences: { theme: "dark", notifications: true, language: "en", }, roles: ["admin", "user"], metadata: { createdAt: new Date(), updatedAt: new Date(), }, }; 

4.4 如何优化 TypeScript 编译性能?

回答: 优化编译性能的方法:

  1. 使用 skipLibCheck:跳过对声明文件的类型检查
     // tsconfig.json { "compilerOptions": { "skipLibCheck": true } } 
  2. 使用 incremental:启用增量编译
     { "compilerOptions": { "incremental": true } } 
  3. 使用 projectReferences:拆分大型项目
  4. 避免使用 any:类型检查会更快
  5. 使用 outDir:将输出文件分离
  6. 使用 ts-node--transpile-only:开发时跳过类型检查

4.5 如何在运行时访问类型信息?

回答: TypeScript 在编译后会擦除类型信息,但可以通过以下方式在运行时访问:

  1. 使用类型守卫
     function isString(value: unknown): value is string { return typeof value === "string"; } 
  2. 使用 instanceof: “`typescript class MyClass { constructor(public name: string) {} }

function checkType(obj: unknown): obj is MyClass {

 return obj instanceof MyClass; 

}

3. **使用自定义属性**: ```typescript interface TypedValue<T> { value: T; type: string; } const typedValue: TypedValue<string> = { value: "hello", type: "string", }; 

4.6 如何处理异步代码的类型?

回答: 使用 Promise 和 async/await 的类型:

// Promise 类型 function fetchData(): Promise<string> { return new Promise((resolve) => { setTimeout(() => resolve("data"), 1000); }); } // async 函数返回类型 async function getData(): Promise<string> { const result = await fetchData(); return result; } // 处理多个异步操作 async function fetchMultiple(): Promise<[string, number]> { const data = await fetchData(); const count = await fetchCount(); return [data, count]; } // 错误处理 async function safeFetch(): Promise<string | Error> { try { return await fetchData(); } catch (error) { return error as Error; } } 

4.7 如何处理动态对象键?

回答: 使用索引签名和泛型:

// 使用索引签名 interface DynamicObject { [key: string]: any; } const obj: DynamicObject = { name: "Alice", age: 25, city: "New York", }; // 使用泛型和索引签名 function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user = { name: "Alice", age: 25 }; const userName = getProperty(user, "name"); // string const userAge = getProperty(user, "age"); // number // 使用 Record 类型 type Config = Record<string, string>; const config: Config = { apiUrl: "https://api.example.com", timeout: "5000", }; 

4.8 如何处理函数重载?

回答: TypeScript 支持函数重载,但实现时需要小心:

// 函数重载声明 function greet(name: string): string; function greet(name: string, age: number): string; function greet(name: string, age?: number): string { if (age !== undefined) { return `Hello, ${name}! You are ${age} years old.`; } return `Hello, ${name}!`; } // 使用示例 console.log(greet("Alice")); // "Hello, Alice!" console.log(greet("Bob", 30)); // "Hello, Bob! You are 30 years old." // 处理不同参数类型 function process(value: string): string; function process(value: number): number; function process(value: boolean): boolean; function process(value: string | number | boolean): string | number | boolean { if (typeof value === "string") { return value.toUpperCase(); } else if (typeof value === "number") { return value * 2; } else { return !value; } } 

4.9 如何处理大型项目的类型定义?

回答: 大型项目的类型管理策略:

  1. 模块化类型定义: “`typescript // types/user.ts export interface User { id: number; name: string; }

// types/product.ts export interface Product {

 id: number; title: string; price: number; 

}

// types/index.ts export * from ‘./user’; export * from ‘./product’;

2. **使用命名空间**: ```typescript namespace MyApp { export interface Config { apiUrl: string; } export class Service { constructor(public config: Config) {} } } 
  1. 使用 tsconfig.jsonpaths
     { "compilerOptions": { "paths": { "@types/*": ["./src/types/*"], "@components/*": ["./src/components/*"] } } } 
  2. 使用类型生成工具:如 graphql-code-generator 从 GraphQL schema 生成类型

4.10 如何处理 JSON 数据的类型?

回答: JSON 数据的类型处理:

// 定义 JSON 类型 type JSONValue = | string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue }; // 解析 JSON 的类型安全函数 function parseJSON<T>(jsonString: string): T { return JSON.parse(jsonString) as T; } // 使用示例 interface User { id: number; name: string; email: string; } const userJson = '{"id": 1, "name": "Alice", "email": "alice@example.com"}'; const user = parseJSON<User>(userJson); console.log(user.name); // "Alice" // 处理 API 响应 async function fetchUser(): Promise<User> { const response = await fetch('/api/user'); const data = await response.json(); return data as User; // 类型断言 } // 更安全的处理方式 async function fetchUserSafe(): Promise<User | null> { try { const response = await fetch('/api/user'); if (!response.ok) { return null; } const data = await response.json(); // 可以添加运行时验证 if (typeof data.id === 'number' && typeof data.name === 'string') { return data as User; } return null; } catch (error) { console.error('Failed to fetch user:', error); return null; } } 

5. 总结

TypeScript 的类型系统是一个强大而复杂的工具,从基础类型到高级类型,从类型守卫到泛型,每一种特性都有其特定的使用场景。掌握这些类型特性可以帮助你编写更安全、更可维护的代码。

关键要点:

  1. 基础类型是基石:理解 stringnumberbooleanarraytuple 等基础类型是学习 TypeScript 的第一步。
  2. 高级类型提升能力:联合类型、交叉类型、映射类型、条件类型等高级特性让你能够表达复杂的类型关系。
  3. 类型守卫确保安全:使用类型守卫可以在运行时进行类型检查,确保类型安全。
  4. 泛型增加灵活性:泛型让你能够编写可重用的、类型安全的代码。
  5. 实践是关键:通过实际项目练习,你会更深入地理解 TypeScript 类型系统的强大之处。

进一步学习建议:

  1. 阅读官方文档:TypeScript 官方文档是学习的最佳资源。
  2. 参与开源项目:在 GitHub 上寻找使用 TypeScript 的项目,阅读和贡献代码。
  3. 练习类型挑战:在 Type Challenges 等网站上练习类型编程。
  4. 关注社区:关注 TypeScript 社区的最新动态和最佳实践。

通过系统地学习和实践 TypeScript 的类型系统,你将能够编写出更健壮、更易维护的代码,提升开发效率和代码质量。