轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JavaScript

  • TypeScript

    • TypeScript极简入门
    • TypeScript高级特性详解
      • 一、为什么需要高级类型?
      • 二、交叉类型(&):组合多个类型
        • 基本概念
        • 实际应用:Mixin 模式
        • 注意事项
      • 三、可辨识联合类型:解决开头的问题
        • 什么是可辨识联合?
        • 类型收窄(Type Narrowing)
        • 实际应用:React 组件状态
      • 四、类型索引:从已有类型提取信息
        • keyof:获取所有键
        • 索引访问类型:提取属性类型
        • typeof:从值获取类型
      • 五、条件类型(T extends U ? X : Y)
        • 基本概念
        • 实际应用 1:NonNullable
        • 实际应用 2:函数重载的智能提示
        • 分布式条件类型
      • 六、infer:在条件类型中推断类型
        • 基本概念
        • 实际应用 1:提取函数返回值类型
        • 实际应用 2:提取函数参数类型
        • 实际应用 3:提取 Promise 值类型
        • 实际应用 4:提取元组的第一个和最后一个元素
      • 七、映射类型:批量转换属性
        • 基本概念
        • 内置映射类型
        • 修饰符操作
        • 键名重映射(as 子句)
        • 实际应用:深度只读
      • 八、模板字面量类型
        • 基本用法
        • 内置字符串操作类型
        • 实际应用:生成方法名
        • 实际应用:事件监听
      • 九、类型编程实战
        • 场景 1:实现 DeepPartial
        • 场景 2:提取对象中所有字符串属性
        • 场景 3:扁平化嵌套数组类型
        • 场景 4:联合类型转元组
      • 十、装饰器(Experimental)
        • 什么是装饰器?
        • 类装饰器
        • 方法装饰器
        • 属性装饰器
        • 实际应用:自动记录日志
      • 十一、声明文件(.d.ts)
        • 为第三方库添加类型
        • 扩展全局对象
        • 扩展现有模块
      • 十二、实用工具类型总结
        • 对象相关
        • 联合类型相关
        • 函数相关
      • 十三、最佳实践
        • 1. 优先使用 interface 而非 type
        • 2. 避免过度使用 any
        • 3. 使用 const 断言
        • 4. 善用类型守卫
        • 5. 合理使用泛型约束
      • 十四、总结
  • Node.js

  • Vue.js

  • 工程化

  • 浏览器与Web API

  • 前端
  • TypeScript
轩辕李
2025-05-09
目录

TypeScript高级特性详解

本文深入浅出地讲解 TypeScript 的高级特性。我们会从实际问题出发,循序渐进地介绍每个概念,让你真正理解这些高级特性的用途和使用方法。

# 一、为什么需要高级类型?

在学习具体特性之前,先理解我们要解决什么问题。

假设你在开发一个 API 客户端,需要处理不同的响应状态:

// 不好的设计:使用联合类型但没有类型区分
interface ApiResponse {
    status: 'success' | 'error' | 'loading';
    data?: any;
    error?: string;
}

function handleResponse(response: ApiResponse) {
    if (response.status === 'success') {
        console.log(response.data.toUpperCase());  // 危险!data 可能不存在
    }
}

问题在于:

  1. TypeScript 无法知道 status 为 'success' 时,data 一定存在
  2. 我们需要更精确的类型系统来表达这种关系

这就是高级类型要解决的问题。

# 二、交叉类型(&):组合多个类型

# 基本概念

交叉类型就像"并集",把多个类型的属性合并到一起:

interface HasName {
    name: string;
}

interface HasAge {
    age: number;
}

// Person 必须同时拥有 name 和 age
type Person = HasName & HasAge;

const person: Person = {
    name: 'Alice',
    age: 30
};

类比理解:就像一个人既是工程师(有编程技能)又是设计师(有设计技能),交叉类型表示"同时是"。

# 实际应用:Mixin 模式

在类中混入多个功能:

// 时间戳功能
interface Timestamped {
    createdAt: Date;
    updatedAt: Date;
}

// 软删除功能
interface SoftDeletable {
    isDeleted: boolean;
    deletedAt?: Date;
}

// 用户数据
interface UserData {
    id: number;
    name: string;
}

// 组合所有功能
type User = UserData & Timestamped & SoftDeletable;

const user: User = {
    id: 1,
    name: 'Alice',
    createdAt: new Date(),
    updatedAt: new Date(),
    isDeleted: false
};

# 注意事项

交叉类型处理冲突属性时会变成 never:

interface A {
    value: string;
}

interface B {
    value: number;
}

type C = A & B;  // { value: never },因为 value 不能同时是 string 和 number

# 三、可辨识联合类型:解决开头的问题

# 什么是可辨识联合?

通过一个公共的字面量属性来区分不同的类型:

// 每个接口都有 type 属性,但值不同
interface Success {
    type: 'success';
    data: string;
}

interface Error {
    type: 'error';
    errorMessage: string;
}

interface Loading {
    type: 'loading';
}

// 联合类型
type ApiResponse = Success | Error | Loading;

# 类型收窄(Type Narrowing)

TypeScript 会根据 type 字段自动推断具体类型:

function handleResponse(response: ApiResponse) {
    switch (response.type) {
        case 'success':
            // 在这里,TypeScript 知道 response 是 Success 类型
            console.log(response.data.toUpperCase());  // 安全!
            // console.log(response.errorMessage);  // 错误:Success 没有 errorMessage
            break;
            
        case 'error':
            // 在这里,TypeScript 知道 response 是 Error 类型
            console.log(response.errorMessage);  // 安全!
            // console.log(response.data);  // 错误:Error 没有 data
            break;
            
        case 'loading':
            console.log('Loading...');
            break;
    }
}

关键点:

  • type 属性是字面量类型('success' 而非 string)
  • TypeScript 通过 type 的值判断具体是哪个类型
  • 这种模式在 Redux、状态机等场景中非常常见

# 实际应用:React 组件状态

interface IdleState {
    status: 'idle';
}

interface LoadingState {
    status: 'loading';
    progress: number;
}

interface SuccessState {
    status: 'success';
    data: string[];
}

interface ErrorState {
    status: 'error';
    error: Error;
}

type DataState = IdleState | LoadingState | SuccessState | ErrorState;

function renderUI(state: DataState) {
    switch (state.status) {
        case 'idle':
            return 'Click to load';
        case 'loading':
            return `Loading... ${state.progress}%`;  // 可以访问 progress
        case 'success':
            return state.data.join(', ');  // 可以访问 data
        case 'error':
            return `Error: ${state.error.message}`;  // 可以访问 error
    }
}

# 四、类型索引:从已有类型提取信息

# keyof:获取所有键

interface User {
    id: number;
    name: string;
    email: string;
}

type UserKeys = keyof User;  // 'id' | 'name' | 'email'

实际应用:编写通用的属性访问函数

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const user: User = { id: 1, name: 'Alice', email: 'alice@example.com' };

const name = getProperty(user, 'name');   // TypeScript 知道返回 string
const id = getProperty(user, 'id');       // TypeScript 知道返回 number
// const age = getProperty(user, 'age');  // 错误:'age' 不存在

# 索引访问类型:提取属性类型

interface User {
    id: number;
    name: string;
    address: {
        city: string;
        country: string;
    };
}

type NameType = User['name'];  // string
type AddressType = User['address'];  // { city: string; country: string; }
type CityType = User['address']['city'];  // string

// 提取多个属性的类型(返回联合类型)
type NameOrId = User['name' | 'id'];  // string | number

实际应用:从数组提取元素类型

const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
];

type User = typeof users[number];  // { id: number; name: string; }

# typeof:从值获取类型

const config = {
    apiUrl: 'https://api.example.com',
    timeout: 5000,
    retries: 3
};

type Config = typeof config;
// { apiUrl: string; timeout: number; retries: number; }

// 实际应用:从函数获取返回值类型
function createUser(name: string, age: number) {
    return { name, age, createdAt: new Date() };
}

type User = ReturnType<typeof createUser>;
// { name: string; age: number; createdAt: Date; }

# 五、条件类型(T extends U ? X : Y)

# 基本概念

条件类型就像三元运算符,根据类型关系返回不同类型:

// 如果 T 是 string,返回 'yes',否则返回 'no'
type IsString<T> = T extends string ? 'yes' : 'no';

type A = IsString<string>;   // 'yes'
type B = IsString<number>;   // 'no'
type C = IsString<any>;      // 'yes'(any 可以赋值给任何类型)

类比理解:就像 const result = condition ? 'yes' : 'no',但是在类型层面。

# 实际应用 1:NonNullable

去除 null 和 undefined:

type NonNullable<T> = T extends null | undefined ? never : T;

type A = NonNullable<string | null | undefined>;  // string
type B = NonNullable<number | null>;  // number

# 实际应用 2:函数重载的智能提示

// 根据参数类型返回不同类型
type ReturnValue<T> = T extends 'string'
    ? string
    : T extends 'number'
    ? number
    : never;

function getValue<T extends 'string' | 'number'>(type: T): ReturnValue<T> {
    if (type === 'string') {
        return 'hello' as ReturnValue<T>;
    } else {
        return 42 as ReturnValue<T>;
    }
}

const str = getValue('string');  // string
const num = getValue('number');  // number

# 分布式条件类型

当条件类型遇到联合类型时,会分别应用到每个成员:

type ToArray<T> = T extends any ? T[] : never;

type Result = ToArray<string | number>;
// 展开过程:
// ToArray<string> | ToArray<number>
// string[] | number[]

实际应用:提取联合类型中的某些成员

type ExtractString<T> = T extends string ? T : never;

type Result = ExtractString<string | number | boolean>;  // string

# 六、infer:在条件类型中推断类型

# 基本概念

infer 关键字让我们在条件类型中"捕获"某个类型:

// 提取数组元素类型
type ArrayElement<T> = T extends (infer E)[] ? E : never;

type A = ArrayElement<string[]>;  // string
type B = ArrayElement<number[]>;  // number
type C = ArrayElement<string>;    // never(不是数组)

理解步骤:

  1. 检查 T 是否是数组类型 (infer E)[]
  2. 如果是,infer E 会"推断"出元素类型,并赋值给 E
  3. 返回推断出的 E

# 实际应用 1:提取函数返回值类型

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function getUser() {
    return { id: 1, name: 'Alice' };
}

type User = ReturnType<typeof getUser>;  // { id: number; name: string; }

解读:

  • (...args: any[]) 匹配任意函数
  • infer R 推断返回值类型
  • 返回推断出的 R

# 实际应用 2:提取函数参数类型

type Parameters<T> = T extends (...args: infer P) => any ? P : never;

function createUser(name: string, age: number) {
    return { name, age };
}

type Params = Parameters<typeof createUser>;  // [name: string, age: number]

# 实际应用 3:提取 Promise 值类型

type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

type A = UnwrapPromise<Promise<string>>;  // string
type B = UnwrapPromise<number>;           // number

// 递归提取嵌套 Promise
type DeepUnwrapPromise<T> = T extends Promise<infer U>
    ? DeepUnwrapPromise<U>
    : T;

type C = DeepUnwrapPromise<Promise<Promise<string>>>;  // string

# 实际应用 4:提取元组的第一个和最后一个元素

// 提取第一个元素
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;

type A = First<[string, number, boolean]>;  // string

// 提取最后一个元素
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;

type B = Last<[string, number, boolean]>;  // boolean

# 七、映射类型:批量转换属性

# 基本概念

映射类型可以批量修改对象的所有属性:

interface User {
    id: number;
    name: string;
    email: string;
}

// 将所有属性变为只读
type ReadonlyUser = {
    readonly [K in keyof User]: User[K];
};

// 等价于:
// {
//     readonly id: number;
//     readonly name: string;
//     readonly email: string;
// }

语法解读:

  • K in keyof User:遍历 User 的所有键('id' | 'name' | 'email')
  • User[K]:获取对应的值类型

# 内置映射类型

TypeScript 提供了常用的映射类型:

// 1. Partial:所有属性变为可选
type PartialUser = Partial<User>;
// {
//     id?: number;
//     name?: string;
//     email?: string;
// }

// 2. Required:所有属性变为必需
type RequiredUser = Required<PartialUser>;

// 3. Readonly:所有属性变为只读
type ReadonlyUser = Readonly<User>;

// 4. Pick:选取部分属性
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string; }

// 5. Omit:排除部分属性
type PublicUser = Omit<User, 'email'>;
// { id: number; name: string; }

# 修饰符操作

// 移除 readonly 修饰符
type Mutable<T> = {
    -readonly [K in keyof T]: T[K];
};

// 移除可选修饰符
type Concrete<T> = {
    [K in keyof T]-?: T[K];
};

interface User {
    readonly id: number;
    name?: string;
}

type MutableUser = Mutable<User>;  // { id: number; name?: string; }
type ConcreteUser = Concrete<User>;  // { readonly id: number; name: string; }

# 键名重映射(as 子句)

可以在映射时修改键名:

// 从属性名生成 getter 方法
type Getters<T> = {
    [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

interface User {
    name: string;
    age: number;
}

type UserGetters = Getters<User>;
// {
//     getName: () => string;
//     getAge: () => number;
// }

// 过滤属性
type RemoveId<T> = {
    [K in keyof T as Exclude<K, 'id'>]: T[K];
};

type UserWithoutId = RemoveId<User>;  // { name: string; age: number; }

# 实际应用:深度只读

type DeepReadonly<T> = {
    readonly [K in keyof T]: T[K] extends object
        ? DeepReadonly<T[K]>
        : T[K];
};

interface User {
    name: string;
    address: {
        city: string;
        country: string;
    };
}

type ReadonlyUser = DeepReadonly<User>;
// {
//     readonly name: string;
//     readonly address: {
//         readonly city: string;
//         readonly country: string;
//     };
// }

# 八、模板字面量类型

# 基本用法

在类型中使用模板字符串:

type World = "world";
type Greeting = `hello ${World}`;  // "hello world"

// 联合类型会展开
type Color = "red" | "blue";
type Size = "small" | "large";

type ColoredSize = `${Color}-${Size}`;
// "red-small" | "red-large" | "blue-small" | "blue-large"

# 内置字符串操作类型

TypeScript 提供了四个内置的字符串操作类型:

// 1. Uppercase:转大写
type Upper = Uppercase<"hello">;  // "HELLO"

// 2. Lowercase:转小写
type Lower = Lowercase<"HELLO">;  // "hello"

// 3. Capitalize:首字母大写
type Cap = Capitalize<"hello">;   // "Hello"

// 4. Uncapitalize:首字母小写
type Uncap = Uncapitalize<"Hello">;  // "hello"

# 实际应用:生成方法名

// 从属性名自动生成 setter 方法名
type Setters<T> = {
    [K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};

interface User {
    name: string;
    age: number;
}

type UserSetters = Setters<User>;
// {
//     setName: (value: string) => void;
//     setAge: (value: number) => void;
// }

# 实际应用:事件监听

type EventListener<T> = {
    on<K extends string & keyof T>(
        event: `${K}Changed`,
        callback: (newValue: T[K]) => void
    ): void;
};

declare function watch<T>(obj: T): T & EventListener<T>;

const user = watch({
    name: 'Alice',
    age: 30
});

// 正确:监听 nameChanged 事件
user.on('nameChanged', (newName) => {
    console.log(newName.toUpperCase());  // newName 是 string
});

// 正确:监听 ageChanged 事件
user.on('ageChanged', (newAge) => {
    console.log(newAge + 1);  // newAge 是 number
});

// 错误:事件名不对
// user.on('name', () => {});

# 九、类型编程实战

# 场景 1:实现 DeepPartial

让对象的所有层级都变为可选:

type DeepPartial<T> = T extends object
    ? {
        [K in keyof T]?: DeepPartial<T[K]>;
      }
    : T;

interface User {
    id: number;
    profile: {
        name: string;
        address: {
            city: string;
            country: string;
        };
    };
}

type PartialUser = DeepPartial<User>;

const user: PartialUser = {
    profile: {
        address: {
            city: 'Beijing'  // 只提供部分字段
        }
    }
};

# 场景 2:提取对象中所有字符串属性

type StringKeys<T> = {
    [K in keyof T]: T[K] extends string ? K : never;
}[keyof T];

interface User {
    id: number;
    name: string;
    email: string;
    age: number;
}

type UserStringKeys = StringKeys<User>;  // 'name' | 'email'

理解步骤:

  1. { [K in keyof T]: T[K] extends string ? K : never } 生成:
    {
        id: never;
        name: 'name';
        email: 'email';
        age: never;
    }
    
  2. [keyof T] 提取所有值:never | 'name' | 'email' | never
  3. 联合类型中的 never 会被自动忽略,得到 'name' | 'email'

# 场景 3:扁平化嵌套数组类型

type Flatten<T> = T extends Array<infer U>
    ? U extends Array<any>
        ? Flatten<U>
        : U
    : T;

type Nested = [1, [2, [3, [4]]]];
type Flat = Flatten<Nested>;  // 1 | 2 | 3 | 4

# 场景 4:联合类型转元组

// 这是一个高级技巧,理解起来较复杂
type UnionToIntersection<U> = (
    U extends any ? (x: U) => void : never
) extends (x: infer I) => void
    ? I
    : never;

type LastInUnion<U> = UnionToIntersection<
    U extends any ? (x: U) => void : never
> extends (x: infer L) => void
    ? L
    : never;

type UnionToTuple<U, Last = LastInUnion<U>> = [U] extends [never]
    ? []
    : [...UnionToTuple<Exclude<U, Last>>, Last];

type Result = UnionToTuple<'a' | 'b' | 'c'>;  // ['a', 'b', 'c']

说明:这个例子较为复杂,主要展示 TypeScript 类型系统的强大表达能力,实际开发中较少用到。

# 十、装饰器(Experimental)

# 什么是装饰器?

装饰器是一种特殊的声明,可以附加到类、方法、属性或参数上,用于修改它们的行为。

启用装饰器:在 tsconfig.json 中添加:

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

# 类装饰器

// 密封类,防止添加新属性
function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

@sealed
class Greeter {
    greeting: string;
    
    constructor(message: string) {
        this.greeting = message;
    }
}

const greeter = new Greeter('Hello');
// (greeter as any).newProp = 'test';  // 运行时错误(严格模式下)

# 方法装饰器

// 性能监控装饰器
function measure(
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
) {
    const originalMethod = descriptor.value;
    
    descriptor.value = function(...args: any[]) {
        const start = performance.now();
        const result = originalMethod.apply(this, args);
        const end = performance.now();
        console.log(`${propertyKey} 耗时: ${(end - start).toFixed(2)}ms`);
        return result;
    };
}

class Calculator {
    @measure
    fibonacci(n: number): number {
        if (n <= 1) return n;
        return this.fibonacci(n - 1) + this.fibonacci(n - 2);
    }
}

const calc = new Calculator();
calc.fibonacci(30);  // 输出:fibonacci 耗时: 5.23ms

# 属性装饰器

// 验证装饰器
function Min(min: number) {
    return function(target: any, propertyKey: string) {
        let value: number;
        
        Object.defineProperty(target, propertyKey, {
            get() {
                return value;
            },
            set(newValue: number) {
                if (newValue < min) {
                    throw new Error(`${propertyKey} 不能小于 ${min}`);
                }
                value = newValue;
            }
        });
    };
}

class Product {
    @Min(0)
    price: number = 0;
}

const product = new Product();
product.price = 100;   // 正确
// product.price = -10;  // 运行时错误

# 实际应用:自动记录日志

function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    
    descriptor.value = function(...args: any[]) {
        console.log(`调用 ${propertyKey},参数:`, args);
        const result = originalMethod.apply(this, args);
        console.log(`${propertyKey} 返回:`, result);
        return result;
    };
}

class UserService {
    @log
    createUser(name: string, age: number) {
        return { id: 1, name, age };
    }
}

const service = new UserService();
service.createUser('Alice', 30);
// 输出:
// 调用 createUser,参数: ['Alice', 30]
// createUser 返回: { id: 1, name: 'Alice', age: 30 }

# 十一、声明文件(.d.ts)

# 为第三方库添加类型

假设你使用了一个没有类型定义的 JavaScript 库:

// 创建 lodash.d.ts
declare module 'lodash' {
    export function chunk<T>(array: T[], size: number): T[][];
    export function compact<T>(array: T[]): T[];
}

// 现在可以有类型提示了
import { chunk } from 'lodash';
const result = chunk([1, 2, 3, 4], 2);  // number[][]

# 扩展全局对象

// global.d.ts
declare global {
    interface Window {
        myApp: {
            version: string;
            init: () => void;
        };
    }
}

export {};  // 必须有这行,否则不会被视为模块

// 使用
window.myApp.version = '1.0.0';
window.myApp.init();

# 扩展现有模块

// 扩展 Express Request 对象
import 'express';

declare module 'express' {
    interface Request {
        user?: {
            id: number;
            name: string;
        };
    }
}

// 使用
import express from 'express';
const app = express();

app.get('/', (req, res) => {
    if (req.user) {
        res.send(`Hello, ${req.user.name}`);
    }
});

# 十二、实用工具类型总结

# 对象相关

// Partial:所有属性可选
type Partial<T> = { [K in keyof T]?: T[K] };

// Required:所有属性必需
type Required<T> = { [K in keyof T]-?: T[K] };

// Readonly:所有属性只读
type Readonly<T> = { readonly [K in keyof T]: T[K] };

// Pick:选取属性
type Pick<T, K extends keyof T> = { [P in K]: T[P] };

// Omit:排除属性
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

// Record:创建对象类型
type Record<K extends string | number | symbol, T> = { [P in K]: T };

# 联合类型相关

// Exclude:从联合类型中排除
type Exclude<T, U> = T extends U ? never : T;

// Extract:从联合类型中提取
type Extract<T, U> = T extends U ? T : never;

// NonNullable:排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T;

# 函数相关

// ReturnType:提取返回值类型
type ReturnType<T extends (...args: any) => any> =
    T extends (...args: any) => infer R ? R : any;

// Parameters:提取参数类型
type Parameters<T extends (...args: any) => any> =
    T extends (...args: infer P) => any ? P : never;

// ConstructorParameters:提取构造函数参数类型
type ConstructorParameters<T extends abstract new (...args: any) => any> =
    T extends abstract new (...args: infer P) => any ? P : never;

// InstanceType:提取构造函数返回类型
type InstanceType<T extends abstract new (...args: any) => any> =
    T extends abstract new (...args: any) => infer R ? R : any;

# 十三、最佳实践

# 1. 优先使用 interface 而非 type

// ✅ 推荐
interface User {
    id: number;
    name: string;
}

// interface 可以声明合并
interface User {
    email: string;
}

// ❌ type 不能重复声明
type User = {
    id: number;
    name: string;
};

何时使用 type:

  • 联合类型:type ID = string | number
  • 元组:type Point = [number, number]
  • 映射类型:type Readonly<T> = { readonly [K in keyof T]: T[K] }

# 2. 避免过度使用 any

// ❌ 避免
function process(data: any) {
    return data.value;
}

// ✅ 使用 unknown(需要类型检查)
function process(data: unknown) {
    if (typeof data === 'object' && data !== null && 'value' in data) {
        return (data as { value: any }).value;
    }
}

// ✅ 使用泛型
function process<T extends { value: any }>(data: T) {
    return data.value;
}

# 3. 使用 const 断言

// ❌ 类型过于宽泛
const routes = {
    home: '/',
    about: '/about'
};
// routes: { home: string; about: string; }

// ✅ 使用 const 断言
const routes = {
    home: '/',
    about: '/about'
} as const;
// routes: { readonly home: "/"; readonly about: "/about"; }

# 4. 善用类型守卫

function isString(value: unknown): value is string {
    return typeof value === 'string';
}

function process(value: unknown) {
    if (isString(value)) {
        console.log(value.toUpperCase());  // value 是 string
    }
}

# 5. 合理使用泛型约束

// ❌ 太宽泛
function merge<T, U>(obj1: T, obj2: U) {
    return { ...obj1, ...obj2 };
}

// ✅ 约束为对象类型
function merge<T extends object, U extends object>(obj1: T, obj2: U) {
    return { ...obj1, ...obj2 };
}

merge({ a: 1 }, { b: 2 });  // 正确
// merge(1, 2);  // 错误

# 十四、总结

TypeScript 的高级特性虽然看起来复杂,但都是为了解决实际问题:

  1. 交叉类型(&):组合多个类型
  2. 可辨识联合:安全地处理不同状态
  3. 类型索引(keyof、typeof):从已有类型提取信息
  4. 条件类型:根据条件返回不同类型
  5. infer 关键字:在条件类型中推断类型
  6. 映射类型:批量转换对象属性
  7. 模板字面量类型:操作字符串类型
  8. 装饰器:修改类和方法的行为

掌握这些特性后,你可以:

  • 编写更加类型安全的代码
  • 减少运行时错误
  • 获得更好的 IDE 智能提示
  • 构建更灵活的类型系统

建议循序渐进地学习,从实际问题出发,逐步掌握这些高级特性。结合 TypeScript极简入门,你将能够充分发挥 TypeScript 的强大能力。

祝你变得更强!

编辑 (opens new window)
#TypeScript#JavaScript
上次更新: 2025/11/07
TypeScript极简入门
Node.js快速入门

← TypeScript极简入门 Node.js快速入门→

最近更新
01
AI编程时代的一些心得
09-11
02
Claude Code与Codex的协同工作
09-01
03
Claude Code 最佳实践(个人版)
08-01
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式