前置知识: TypeScript

高级类型与类型演算

12 minAdvanced

映射类型、条件类型、模板字面量类型与类型体操。

1. 型断言 (Type Assertions)

型断言允许我们手动告诉编译器一个值的具体型,当我们比编译器更了解变量的型时非常有用。

1.1 基本语法

TypeScript 提供了两种型断言语法:

// 推荐语法:as 语法
let someValue: unknown = 'this is a string';
let strLength: number = (someValue as string).length;
// 角括号语法(在 JSX 中不推荐使用)
let someValue: unknown = 'this is a string';
let strLength: number = (<string>someValue).length;

1.2 型断言的使用场景

1.2.1 从 unknown 型断言为具体

function processValue(value: unknown): void {
  // 类型断言为 string
  if (typeof value === 'string') {
    console.log((value as string).toUpperCase());
  }
  // 类型断言为 number
  if (typeof value === 'number') {
    console.log((value as number).toFixed(2));
  }
}
processValue('hello'); // 输出: HELLO
processValue(42); // 输出: 42.00

1.2.2 从联合类型断言为具体

interface Cat {
  meow(): void;
  ;
}
interface Dog {
  bark(): void;
  ;
}
type Animal = Cat | Dog;
function makeSound(animal: Animal): void {
  // 类型断言为 Cat
  if ((animal as Cat).meow) {
    (animal as Cat).meow();
  } else {
    // 类型断言为 Dog
    (animal as Dog).bark();
  }
  ;
}
const cat: Cat = { meow: () => console.log('Meow!') };
const dog: Dog = { bark: () => console.log('Woof!') };
makeSound(cat); // 输出: Meow!
makeSound(dog); // 输出: Woof!

1.2.3 断言为更具体的

interface Person {
  name: string;
  age: number;
}
interface Employee extends Person {
  employeeId: number;
  department: string;
}
function getEmployeeInfo(person: Person): void {
  // 断言为 Employee 类型
  const employee = person as Employee;
  console.log(`Name: ${employee.name}, ID: ${employee.employeeId}`);
}
const employee: Employee = {
  name: 'Alice',
  age: 30,
  employeeId: 12345,
  department: 'Engineering',
};
getEmployeeInfo(employee); // 输出: Name: Alice, ID: 12345

1.3 型断言的最佳实践

  • 只在必要时使用: 型断言会绕过 TypeScript 的型检查,应谨慎使用。
  • 结合类型守卫: 在使用型断言前,最好先进行型检查。
  • 使用 as 语法: 优先使用 as 语法,特别是在 JSX 代码中。
  • 避免过度断言: 不要使用型断言来掩盖真正的型问题。

2. 非空断言 (!)

非空断言操作符 ! 告诉编译器一个值不为 nullundefined,当我们确定一个值不会是 nullundefined 时使用。

2.1 基本用法

// 非空断言
let maybeNull: string | null = 'Hello';
let definitelyString: string = maybeNull!; // 告诉编译器 maybeNull 不为 null
// 访问可能为 null 的对象属性
interface User {
  name: string;
  email?: string;
  ;
}
const user: User = { name: 'Alice' };
const email: string = user.email!; // 告诉编译器 user.email 存在
// 调用可能为 undefined 的方法
interface Greeter {
  greet?: () => void;
  ;
}
const greeter: Greeter = { greet: () => console.log('Hello!') };
greeter.greet!(); // 告诉编译器 greet 方法存在

2.2 非空断言的使用场景

2.2.1 初始化后肯定存在的值

class User {
  private name: string | null = null;
  constructor(name: string) {
    this.setName(name);
  }
  private setName(name: string): void {
    this.name = name;
  }
  public getName(): string {
    // 构造函数中已初始化,肯定不为 null
    return this.name!;
  }
}
const user = new User('Alice');
console.log(user.getName()); // 输出: Alice

2.2.2 经过型检查后的值

function processValue(value: string | null | undefined): void {
  if (value) {
    // 经过检查后,value 肯定不为 null 或 undefined
    console.log(value!.length); // 非空断言
  }
}
processValue('Hello'); // 输出: 5
processValue(null); // 无输出
processValue(undefined); // 无输出

2.3 非空断言的注意事项

  • 运行时风险: 非空断言只在编译时有效,运行时如果值为 nullundefined,会导致运行时错误。
  • 谨慎使用: 只在确定值不为 nullundefined 时使用。
  • 替代方案: 优先使用类型守卫或可选链操作符 (?.) 来处理可能为 nullundefined 的值。

3. 类型守卫 (Type Guards)

类型守卫是一种运行时检查,用于确定变量的具体型,帮助编译器进行型缩小。

3.1 内置类型守卫

3.1.1 typeof 类型守卫

function processValue(value: string | number | boolean): void {
  if (typeof value === 'string') {
    // 类型缩小为 string
    console.log(value.toUpperCase());
  } else if (typeof value === 'number') {
    // 类型缩小为 number
    console.log(value.toFixed(2));
  } else if (typeof value === 'boolean') {
    // 类型缩小为 boolean
    console.log(value ? '' : 'false');
  }
}
processValue('hello'); // 输出: HELLO
processValue(42); // 输出: 42.00
processValue(true); // 输出:

3.1.2 instanceof 类型守卫

class Animal {
  move(): void {
    console.log('Moving...');
  }
}
class Dog extends Animal {
  bark(): void {
    console.log('Woof!');
  }
}
class Cat extends Animal {
  meow(): void {
    console.log('Meow!');
  }
}
function makeSound(animal: Animal): void {
  if (animal instanceof Dog) {
    // 类型缩小为 Dog
    animal.bark();
  } else if (animal instanceof Cat) {
    // 类型缩小为 Cat
    animal.meow();
  } else {
    animal.move();
  }
}
const dog = new Dog();
const cat = new Cat();
const animal = new Animal();
makeSound(dog); // 输出: Woof!
makeSound(cat); // 输出: Meow!
makeSound(animal); // 输出: Moving...

3.1.3 in 操作符类型守卫

interface Cat {
  meow: () => void;
  ;
}
interface Dog {
  bark: () => void;
  ;
}
type Animal = Cat | Dog;
function makeSound(animal: Animal): void {
  if ('meow' in animal) {
    // 类型缩小为 Cat
    animal.meow();
  } else if ('bark' in animal) {
    // 类型缩小为 Dog
    animal.bark();
  }
  ;
}
const cat: Cat = { meow: () => console.log('Meow!') };
const dog: Dog = { bark: () => console.log('Woof!') };
makeSound(cat); // 输出: Meow!
makeSound(dog); // 输出: Woof!

3.2 自定义类型守卫

自定义类型守卫使用 is 关键字来定义一个函数,该函数返回一个布尔值,用于确定变量的型。

// 自定义类型守卫
function isString(value: any): value is string {
  return typeof value === 'string';
}
function isNumber(value: any): value is number {
  return typeof value === 'number';
}
function isBoolean(value: any): value is boolean {
  return typeof value === 'boolean';
}
function processValue(value: unknown): void {
  if (isString(value)) {
    // 类型缩小为 string
    console.log(value.toUpperCase());
  } else if (isNumber(value)) {
    // 类型缩小为 number
    console.log(value.toFixed(2));
  } else if (isBoolean(value)) {
    // 类型缩小为 boolean
    console.log(value ? '' : 'false');
  } else {
    console.log('Unknown type');
  }
}
processValue('hello'); // 输出: HELLO
processValue(42); // 输出: 42.00
processValue(true); // 输出:
processValue(null); // 输出: Unknown type

3.3 类型守卫的最佳实践

  • 明确类型检查: 类型守卫应该明确检查变量的型,避免模糊的检查。
  • 组合使用: 可以组合使用多种类型守卫来处理复杂的型场景。
  • 可读性: 自定义类型守卫函数应该有清晰的名称,说明其检查的型。
  • 性能考虑: 类型守卫在运行时执行,应避免过于复杂的检查逻辑。

4. 映射型 (Mapped Types)

映射型允许我们根据现有型创建新型,通过遍历现有型的属性并应用转换。

4.1 基本映射

// 基本映射类型
interface Person {
  name: string;
  age: number;
  email: string;
}
// 只读映射类型
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];
};
// 使用示例
const readonlyPerson: Readonly<Person> = {
  name: 'Alice',
  age: 30,
  email: 'alice@example.com',
};
// readonlyPerson.name = "Bob"; // 编译错误
const partialPerson: Partial<Person> = {
  name: 'Bob',
};
const requiredPerson: Required<Partial<Person>> = {
  name: 'Charlie',
  age: 25,
  email: 'charlie@example.com',
};

4.2 映射型修饰符

TypeScript 提供了三种映射型修饰符:

  1. readonly: 使属性变为只读
  2. ?: 使属性变为可选
  3. -: 移除修饰符(如 -readonly-?
interface Person {
  readonly name: string;
  age?: number;
  email: string;
}
// 移除 readonly 修饰符
type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};
// 移除可选修饰符
type Required<T> = {
  [P in keyof T]-?: T[P];
};
// 同时移除 readonly 和可选修饰符
type MutableRequired<T> = {
  -readonly [P in keyof T]-?: T[P];
};
// 使用示例
const mutablePerson: Mutable<Person> = {
  name: 'Alice',
  email: 'alice@example.com',
};
mutablePerson.name = 'Bob'; // 现在可以修改
const requiredPerson: Required<Person> = {
  name: 'Charlie',
  age: 30, // 现在必需
  email: 'charlie@example.com',
};

4.3 键重映射

TypeScript 4.1+ 支持键重映射,允许我们在映射型中修改属性键。

interface Person {
  name: string;
  age: number;
  email: string;
}
// 键重映射:添加前缀
type Prefixed<T, Prefix extends string> = {
  [K in keyof T as `${Prefix}${Capitalize<string & K>}`]: T[K];
};
// 键重映射:过滤属性
type OmitByType<T, U> = {
  [K in keyof T as T[K] extends U ? never : K]: T[K];
};
// 使用示例
const prefixedPerson: Prefixed<Person, 'user'> = {
  userName: 'Alice',
  userAge: 30,
  userEmail: 'alice@example.com',
};
const noNumbers: OmitByType<Person, number> = {
  name: 'Bob',
  email: 'bob@example.com',
};

4.4 映射型的应用场景

4.4.1 创建 API 响应

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}
// API 响应类型(移除敏感字段)
type UserResponse = Omit<User, 'password'>;
// API 请求类型(可选字段)
type UserRequest = Partial<Omit<User, 'id'>>;
// 使用示例
const userResponse: UserResponse = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
};
const userRequest: UserRequest = {
  name: 'Bob',
  email: 'bob@example.com',
};

4.4.2 创建配置

interface Config {
  apiUrl: string;
  timeout: number;
  debug: boolean;
}
// 只读配置类型
type ReadonlyConfig = Readonly<Config>;
// 部分配置类型
type PartialConfig = Partial<Config>;
// 使用示例
const defaultConfig: ReadonlyConfig = {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  debug: false,
};
const customConfig: PartialConfig = {
  apiUrl: 'https://api.custom.com',
  timeout: 10000,
};

4.5 映射型的最佳实践

  • 复用现有类型: 利用映射型基于现有型创建新型,减少重复定义。
  • 清晰命名: 为映射型选择清晰、描述性的名称。
  • 合理使用修饰符: 根据需要使用 readonly?- 修饰符。
  • 键重映射: 在需要修改属性键时使用键重映射功能。

5. 条件型 (Conditional Types)

条件型允许我们根据型之间的关系创建新型,语法为 T extends U ? X : Y

5.1 基本条件

 // 基本条件类型
 type IsString<T> = T extends string ?  : false;
 type A = IsString<string>; //
 type B = IsString<number>; // false
 type C = IsString<string | number>; // boolean ( | false)
 // 条件类型与泛型
 function processValue<T>(value: T): T extends string ? string : number {
  if (typeof value === "string") {
  return value.toUpperCase() as any;
  } else {
  return 42 as any;
  }
 }
 const result1 = processValue("hello"); // 类型为 string
 const result2 = processValue(42); // 类型为 number

5.2 条件型与 infer 关键字

infer 关键字允许我们在条件型中推断型,通常用于从复杂型中提取部分型。

// 推断函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
// 推断数组元素类型
type ElementType<T> = T extends Array<infer E> ? E : T;
// 推断元组类型
type First<T> = T extends [infer U, ...any[]] ? U : never;
type Last<T> = T extends [...any[], infer U] ? U : never;
// 使用示例
function add(a: number, b: number): number {
  return a + b;
}
type AddReturnType = ReturnType<typeof add>; // number
type ArrayElement = ElementType<string[]>; // string
type NonArrayElement = ElementType<number>; // number
type FirstElement = First<[string, number, boolean]>; // string
type LastElement = Last<[string, number, boolean]>; // boolean

5.3 条件型的分发特性

当条件型的左侧是一个联合类型时,条件型会自动分发到联合类型的每个成员上。

 // 分发条件类型
 type IsString<T> = T extends string ?  : false;
 type D = IsString<string | number | boolean>; //  | false | false
 // 阻止分发(使用方括号)
 type IsStringNoDistribute<T> = [T] extends [string] ?  : false;
 type E = IsStringNoDistribute<string | number | boolean>; // false

5.4 条件型的应用场景

5.4.1 型过滤

// 从联合类型中过滤出指定类型
type Filter<T, U> = T extends U ? T : never;
type Numbers = Filter<number | string | boolean, number>; // number
type Strings = Filter<number | string | boolean, string>; // string
// 从联合类型中排除指定类型
type Exclude<T, U> = T extends U ? never : T;
type NonNumbers = Exclude<number | string | boolean, number>; // string | boolean

5.4.2 型转换

// 类型转换
type ToArray<T> = T extends any ? T[] : never;
type NumberArray = ToArray<number>; // number[]
type StringArray = ToArray<string>; // string[]
type UnionArray = ToArray<number | string>; // number[] | string[]
// 递归类型转换
type DeepArray<T> = T extends Array<infer U> ? DeepArray<U>[] : T;
type DeepNumberArray = DeepArray<number>; // number
type DeepArrayOfArrays = DeepArray<number[][]>; // number[][][]

5.5 条件型的最佳实践

  • 类型推断: 使用 infer 关键字从复杂型中提取信息。
  • 类型过滤: 使用条件型过滤联合类型中的成员。
  • 类型转换: 使用条件型将一种型转换为另一种型。
  • 递归类型: 使用条件型创建递归型定义。
  • 分发特性: 利用条件型的分发特性处理联合类型

6. 高级型组合

6.1 交叉型 (Intersection Types)

交叉型使用 & 符号,将多个型合并为一个型。

interface Person {
  name: string;
  age: number;
}
interface Employee {
  employeeId: number;
  department: string;
}
// 交叉类型
type EmployeePerson = Person & Employee;
// 使用示例
const employee: EmployeePerson = {
  name: 'Alice',
  age: 30,
  employeeId: 12345,
  department: 'Engineering',
};
console.log(employee.name); // 输出: Alice
console.log(employee.employeeId); // 输出: 12345

6.2 联合类型 (Union Types)

联合类型使用 | 符号,表示一个值可以是多种型中的一种。

// 联合类型
type StringOrNumber = string | number;
type BooleanOrNull = boolean | null;
type ComplexUnion = string | number | boolean | null | undefined;
// 使用示例
function processValue(value: StringOrNumber): void {
  if (typeof value === 'string') {
    console.log(value.toUpperCase());
  } else {
    console.log(value.toFixed(2));
  }
}
processValue('hello'); // 输出: HELLO
processValue(42); // 输出: 42.00

6.3 类型别名接口的结合

// 接口定义
interface Base {
  id: number;
  name: string;
}
// 类型别名与接口结合
type WithTimestamp = Base & {
  createdAt: Date;
  updatedAt: Date;
};
type OptionalBase = Partial<Base>;
type ReadonlyBase = Readonly<Base>;
// 使用示例
const withTimestamp: WithTimestamp = {
  id: 1,
  name: 'Alice',
  createdAt: new Date(),
  updatedAt: new Date(),
};
const optionalBase: OptionalBase = {
  name: 'Bob',
};
const readonlyBase: ReadonlyBase = {
  id: 2,
  name: 'Charlie',
};
// readonlyBase.name = "David"; // 编译错误

7. 型工具

TypeScript 提供了许多内置的型工具,用于常见的型操作。

7.1 常用内置型工具

| 型工具 | 描述 | 示例 | | :------------------------- | :------------------------------------- | :------------------------------------------------------------------------------------ | -------------------------------- | -------------------- | ---------- | ---- | | Partial<T> | 将 T 中所有属性变为可选 | Partial<{ a: number; b: string }>{ a?: number; b?: string } | | Required<T> | 将 T 中所有属性变为必需 | Required<{ a?: number; b?: string }>{ a: number; b: string } | | Readonly<T> | 将 T 中所有属性变为只读 | Readonly<{ a: number; b: string }>{ readonly a: number; readonly b: string } | | Record<K, T> | 构建键为 K 型,值为 T 型的对象型 | Record<string, number>{ [key: string]: number } | | Pick<T, K> | 从 T 中选取指定的属性 K | Pick<{ a: number; b: string; c: boolean }, "a" | "b">{ a: number; b: string } | | Omit<T, K> | 从 T 中排除指定的属性 K | Omit<{ a: number; b: string; c: boolean }, "c">{ a: number; b: string } | | Exclude<T, U> | 从 T 中排除可以赋值给 U 的型 | Exclude<"a" | "b" | "c", "a">"b" | "c" | | Extract<T, U> | 从 T 中提取可以赋值给 U 的型 | Extract<"a" | "b" | "c", "a" | "b">"a" | "b" | | NonNullable<T> | 从 T 中排除 null 和 undefined | NonNullable<string | null | undefined>string | | Parameters<T> | 提取函数 T 的参数型为元组 | Parameters<(a: number, b: string) => void>[number, string] | | ReturnType<T> | 提取函数 T 的返回型 | ReturnType<() => string>string | | InstanceType<T> | 提取构造函数 T 的实例型 | InstanceType<typeof Date>Date | | ThisParameterType<T> | 提取函数 T 的 this 参数型 | ThisParameterType<(this: { x: number }, y: number) => void>{ x: number } | | OmitThisParameter<T> | 从函数 T 中移除 this 参数 | OmitThisParameter<(this: { x: number }, y: number) => void>(y: number) => void |

7.2 自定义型工具

 // 自定义类型工具
 // 深度只读
 type DeepReadonly<T> = T extends object
  ? { readonly [P in keyof T]: DeepReadonly<T[P]> }
  : T;
 // 深度可选
 type DeepPartial<T> = T extends object
  ? { [P in keyof T]?: DeepPartial<T[P]> }
  : T;
 // 深度必填
 type DeepRequired<T> = T extends object
  ? { [P in keyof T]-?: DeepRequired<T[P]> }
  : T;
 // 类型是否为联合类型
 type IsUnion<T> = [T] extends [infer U] ? U extends T ? false :  : false;
 // 获取对象的键类型
 type Keys<T> = keyof T;
 // 获取对象的值类型
 type Values<T> = T[keyof T];
 // 使用示例
 interface ComplexObject {
  name: string;
  age: number;
  address: {
  street: string;
  city: string;
  country: string;
  };
  hobbies: string[];
 }
 const deepReadonly: DeepReadonly<ComplexObject> = {
  name: "Alice",
  age: 30,
  address: {
  street: "123 Main St",
  city: "New York",
  country: "USA"
  },
  hobbies: ["reading", "coding"]
 }
 // deepReadonly.address.city = "Boston"; // 编译错误
 const deepPartial: DeepPartial<ComplexObject> = {
  name: "Bob",
  address: {
  city: "London"
  }
 }
 const deepRequired: DeepRequired<DeepPartial<ComplexObject>> = {
  name: "Charlie",
  age: 25,
  address: {
  street: "456 Oak Ave",
  city: "Paris",
  country: "France"
  },
  hobbies: []
 }
 type TestUnion = IsUnion<string | number>; //
 type TestNonUnion = IsUnion<string>; // false
 type ComplexKeys = Keys<ComplexObject>; // "name" | "age" | "address" | "hobbies"
 type ComplexValues = Values<ComplexObject>; // string | number | { street: string; city: string; country: string; } | string[]

8. 型编程

型编程是使用 TypeScript 的型系统来执行编译时计算和型操作的技术。

8.1 型级别的计算

// 类型级别的计算
// 数字类型的计算
type Add<T extends number, U extends number> = T extends 0 ? U : U extends 0 ? T : never;
type Multiply<T extends number, U extends number> = T extends 0 ? 0 : U extends 0 ? 0 : never;
// 字符串类型的操作
type Concat<T extends string, U extends string> = `${T}${U}`;
type Uppercase<T extends string> = T extends `${infer L}${infer R}`
  ? `${Uppercase<L>}${Uppercase<R>}`
  : T;
// 数组类型的操作
type Reverse<T extends any[]> = T extends [infer F, ...infer R] ? [...Reverse<R>, F] : [];
type Length<T extends any[]> = T['length'];
// 使用示例
type Result1 = Concat<'Hello', ' World'>; // "Hello World"
type Result2 = Reverse<[1, 2, 3, 4, 5]>; // [5, 4, 3, 2, 1]
type Result3 = Length<[1, 2, 3]>; // 3

8.2 型级别的逻辑

 // 类型级别的逻辑
 // 类型相等性检查
 type IsEqual<T, U> = [T] extends [U] ? [U] extends [T] ?  : false : false;
 // 类型包含性检查
 type Includes<T extends any[], U> = T extends [infer F, ...infer R]
  ? IsEqual<F, U> extends
  ?
  : Includes<R, U>
  : false;
 // 类型条件逻辑
 type If<C extends boolean, T, F> = C extends  ? T : F;
 // 使用示例
 type TestEqual1 = IsEqual<string, string>; //
 type TestEqual2 = IsEqual<string, number>; // false
 type TestIncludes1 = Includes<[1, 2, 3, 4, 5], 3>; //
 type TestIncludes2 = Includes<[1, 2, 3, 4, 5], 6>; // false
 type TestIf1 = If<true, string, number>; // string
 type TestIf2 = If<false, string, number>; // number

9. 最佳实践

9.1 型设计原则

  • 类型安全: 优先考虑型安全,避免使用 any 型。
  • 可读性: 设计清晰、易于理解的型。
  • 可维护性: 复用型定义,避免重复。
  • 性能考虑: 注意复杂型可能导致编译时间增加。
  • 渐进式类型: 从简单型开始,逐步添加复杂度。

9.2 型断言与非空断言

  • 谨慎使用: 只在确定型时使用型断言和非空断言。
  • 结合类型守卫: 在使用断言前进行型检查。
  • 替代方案: 优先使用可选链 (?.) 和空值合并 (??) 操作符。

9.3 类型守卫

  • 明确检查: 类型守卫应该明确检查变量的型。
  • 自定义守卫: 为复杂型创建自定义类型守卫
  • 组合使用: 组合多种类型守卫来处理复杂场景。

9.4 映射型与条件

  • 复用现有类型: 使用映射型基于现有型创建新型。
  • 类型推断: 使用 infer 关键字从复杂型中提取信息。
  • 类型过滤: 使用条件型过滤和转换型。
  • 递归类型: 合理使用递归型处理嵌套结构。

9.5 型工具

  • 熟悉内置工具: 充分利用 TypeScript 提供的内置型工具。
  • 创建自定义工具: 根据项目需求创建自定义型工具。
  • 组合使用: 灵活组合多个型工具以满足复杂需求。

10. 代码示例

10.1 型断言与非空断言

// 类型断言示例
function processUnknown(value: unknown): void {
  // 类型断言为 string
  if (typeof value === 'string') {
    const str = value as string;
    console.log(`String length: ${str.length}`);
  }
  // 类型断言为 number
  if (typeof value === 'number') {
    const num = value as number;
    console.log(`Number squared: ${num * num}`);
  }
  // 类型断言为对象
  if (typeof value === 'object' && value !== null) {
    const obj = value as { name: string; age: number };
    console.log(`Object: ${obj.name}, ${obj.age}`);
  }
}
// 非空断言示例
interface User {
  id: number;
  name: string;
  email?: string;
  address?: {
    street: string;
    city: string;
  };
}
function getUserEmail(user: User): string {
  // 非空断言
  return user.email!;
}
function getStreet(user: User): string {
  // 链式非空断言
  return user.address!.street!;
}
// 使用示例
const user: User = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  address: {
    street: '123 Main St',
    city: 'New York',
  },
};
processUnknown('Hello'); // 输出: String length: 5
processUnknown(42); // 输出: Number squared: 1764
processUnknown(user); // 输出: Object: Alice, 1
console.log(getUserEmail(user)); // 输出: alice@example.com
console.log(getStreet(user)); // 输出: 123 Main St

10.2 类型守卫

// 类型守卫示例
// 自定义类型守卫
function isString(value: any): value is string {
  return typeof value === 'string';
  ;
}
function isNumber(value: any): value is number {
  return typeof value === 'number';
  ;
}
function isBoolean(value: any): value is boolean {
  return typeof value === 'boolean';
  ;
}
function isObject(value: any): value is object {
  return typeof value === 'object' && value !== null;
  ;
}
function isArray(value: any): value is any[] {
  return Array.isArray(value);
  ;
}
// 接口类型守卫
interface Person {
  name: string;
  age: number;
  ;
}
function isPerson(value: any): value is Person {
  return isObject(value) && isString((value as Person).name) && isNumber((value as Person).age);
  ;
}
// 使用示例
function processValue(value: unknown): void {
  if (isString(value)) {
    console.log(`String: ${value.toUpperCase()}`);
  } else if (isNumber(value)) {
    console.log(`Number: ${value.toFixed(2)}`);
  } else if (isBoolean(value)) {
    console.log(`Boolean: ${value}`);
  } else if (isArray(value)) {
    console.log(`Array length: ${value.length}`);
  } else if (isPerson(value)) {
    console.log(`Person: ${value.name}, ${value.age}`);
  } else {
    console.log(`Unknown type`);
  }
  ;
}
processValue('Hello'); // 输出: String: HELLO
processValue(42); // 输出: Number: 42.00
processValue(true); // 输出: Boolean:
processValue([1, 2, 3]); // 输出: Array length: 3
processValue({ name: 'Alice', age: 30 }); // 输出: Person: Alice, 30
processValue(null); // 输出: Unknown type

10.3 映射型与条件

 // 映射类型与条件类型示例
 // 基础接口
 interface Product {
  id: number;
  name: string;
  price: number;
  description: string;
  inStock: boolean;
 }
 // 映射类型
 // 只读产品
 type ReadonlyProduct = Readonly<Product>;
 // 可选产品
 type OptionalProduct = Partial<Product>;
 // 产品ID和名称
 type ProductInfo = Pick<Product, "id" | "name">;
 // 产品不含描述
 type ProductWithoutDescription = Omit<Product, "description">;
 // 条件类型
 // 提取字符串属性
 type StringProperties<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
 }
 // 提取数字属性
 type NumberProperties<T> = {
  [K in keyof T as T[K] extends number ? K : never]: T[K];
 }
 // 提取布尔属性
 type BooleanProperties<T> = {
  [K in keyof T as T[K] extends boolean ? K : never]: T[K];
 }
 // 使用示例
 const readonlyProduct: ReadonlyProduct = {
  id: 1,
  name: "Laptop",
  price: 999.99,
  description: "A powerful laptop",
  inStock:
 }
 // readonlyProduct.price = 899.99; // 编译错误
 const optionalProduct: OptionalProduct = {
  id: 2,
  name: "Mouse"
 }
 const productInfo: ProductInfo = {
  id: 3,
  name: "Keyboard"
 }
 const productWithoutDescription: ProductWithoutDescription = {
  id: 4,
  name: "Monitor",
  price: 199.99,
  inStock: false
 }
 const stringProps: StringProperties<Product> = {
  name: "Laptop",
  description: "A powerful laptop"
 }
 const numberProps: NumberProperties<Product> = {
  id: 1,
  price: 999.99
 }
 const booleanProps: BooleanProperties<Product> = {
  inStock:
 }
 console.log(readonlyProduct);
 console.log(optionalProduct);
 console.log(productInfo);
 console.log(productWithoutDescription);
 console.log(stringProps);
 console.log(numberProps);
 console.log(booleanProps);

10.4 高级型组合

// 高级类型组合示例
// 基础类型
interface User {
  id: number;
  name: string;
  email: string;
}
interface Address {
  street: string;
  city: string;
  country: string;
}
interface Order {
  id: number;
  userId: number;
  total: number;
  items: OrderItem[];
}
interface OrderItem {
  productId: number;
  quantity: number;
  price: number;
}
// 高级类型
// 带地址的用户
type UserWithAddress = User & { address: Address };
// 订单详情(包含用户信息)
type OrderWithUser = Order & {
  user: User;
};
// 可选订单项
type OptionalOrderItem = Partial<OrderItem>;
// 只读订单
type ReadonlyOrder = Readonly<Order>;
// 条件类型:提取订单中的产品ID
type ProductIdsFromOrder<T extends Order> = T['items'][number]['productId'];
// 使用示例
const userWithAddress: UserWithAddress = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  address: {
    street: '123 Main St',
    city: 'New York',
    country: 'USA',
  },
};
const orderWithUser: OrderWithUser = {
  id: 101,
  userId: 1,
  total: 1299.98,
  items: [
    { productId: 1, quantity: 1, price: 999.99 },
    { productId: 2, quantity: 2, price: 149.995 },
  ],
  user: {
    id: 1,
    name: 'Alice',
    email: 'alice@example.com',
  },
};
const optionalOrderItem: OptionalOrderItem = {
  productId: 3,
  quantity: 1,
};
const readonlyOrder: ReadonlyOrder = {
  id: 102,
  userId: 2,
  total: 499.99,
  items: [{ productId: 4, quantity: 1, price: 499.99 }],
};
// readonlyOrder.total = 399.99; // 编译错误
// 类型级别提取产品ID
type ProductIds = ProductIdsFromOrder<Order>; // number
console.log(userWithAddress);
console.log(orderWithUser);
console.log(optionalOrderItem);
console.log(readonlyOrder);

更新日志 (Changelog)

  • 2026-04-05: 深入细化 TS 型演算与条件型机制。
  • 2026-04-05: 扩写内容,增加详细的型断言、非空断言、类型守卫、映射型、条件型、高级型组合、型工具、型编程、最佳实践和代码示例等内容。