TypeScript 进阶知识总结

本文深入探讨 TypeScript 类型系统的图灵完备性,涵盖从高级泛型、条件类型、模板字面量类型到元编程(装饰器)和 React 高级模式。

内容分为几个核心模块,每个模块都包含详细的理论解释和生产级代码示例。


模块一:高级泛型与类型体操基础

在高级开发中,泛型不仅仅是 <T>,它涉及到约束(Constraints)、默认值和递归。

1.1 深度递归类型与对象属性修饰

我们需要处理复杂的不可变数据结构或深度可选配置。

ts 复制代码
/**
 * 模块一:高级泛型工具类型
 * 场景:复杂状态管理、配置对象处理
 */

// 1. DeepPartial: 递归地将对象所有属性变为可选
// 这在处理大型配置对象(如 Webpack 配置或图表库配置)时非常有用
type DeepPartial<T> = {
    [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

// 2. DeepReadonly: 递归地将对象所有属性变为只读
// 用于 Redux 的 State 或任何不可变数据结构
type DeepReadonly<T> = {
    readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

// 3. DeepRequired: 递归移除可选属性
type DeepRequired<T> = {
    [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
};

// 4. Mutable: 移除只读属性
type Mutable<T> = {
    -readonly [P in keyof T]: T[P];
};

// --- 测试案例 ---

interface UserSettings {
    theme: {
        mode: 'dark' | 'light';
        palette: {
            primary: string;
            secondary?: string;
        };
    };
    notifications: {
        email: boolean;
        sms?: boolean;
    };
}

// 使用 DeepPartial,允许只配置部分层级
const initialConfig: DeepPartial<UserSettings> = {
    theme: {
        palette: {
            primary: '#007bff'
        }
    }
};

// 使用 DeepReadonly 保护状态
const state: DeepReadonly<UserSettings> = {
    theme: {
        mode: 'dark',
        palette: { primary: '#000', secondary: '#fff' }
    },
    notifications: { email: true, sms: false }
};

// state.theme.mode = 'light'; // 报错:无法分配到 "mode" ,因为它是只读属性。

// --- 进阶:复杂键值过滤 ---

// 获取类型中所有非函数属性的 Key
type NonFunctionKeys<T> = {
    [K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];

// 获取类型中所有函数属性的 Key
type FunctionKeys<T> = {
    [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];

// 仅提取对象中的数据部分
type PickData<T> = Pick<T, NonFunctionKeys<T>>;
// 仅提取对象中的方法部分
type PickMethods<T> = Pick<T, FunctionKeys<T>>;

class UserService {
    id: number = 1;
    name: string = "Admin";
    
    login(): void { console.log("Logging in..."); }
    logout(): void { console.log("Logging out..."); }
    updateProfile(data: PickData<UserService>): void { 
        this.name = data.name;
    }
}

const service = new UserService();
const dataOnly: PickData<UserService> = { id: 2, name: "User" };
// const methodOnly: PickMethods<UserService> = { login: () => {} }; // 正确

模块二:条件类型(Conditional Types)与 infer 关键字

这是 TypeScript 类型编程的核心。通过 infer,我们可以"解包"类型,例如获取 Promise 的返回值、数组的元素类型或函数的参数类型。

2.1 类型推断与解包工具

ts 复制代码
/**
 * 模块二:条件类型与 infer
 * 场景:API 响应处理、函数包装器、Vue/React 类型推导
 */

// 1. Unpacked: 解包数组、Promise 或基本类型
type Unpacked<T> = T extends (infer U)[] 
    ? U 
    : T extends Promise<infer U>
    ? U
    : T;

// 测试 Unpacked
type T0 = Unpacked<string>;  // string
type T1 = Unpacked<string[]>; // string
type T2 = Unpacked<Promise<string>>; // string
type T3 = Unpacked<Promise<string>[]>; // Promise<string>

// 2. GetReturnType: 获取函数返回类型 (内置 ReturnType 的实现原理)
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

// 3. GetParameters: 获取函数参数类型 (内置 Parameters 的实现原理)
type MyParameters<T> = T extends (...args: infer P) => any ? P : never;

// --- 实战:类型安全的 API 请求封装 ---

interface ApiResponse<TData> {
    code: number;
    message: string;
    data: TData;
}

// 模拟后端 API 函数
function getUserInfo(userId: string): Promise<ApiResponse<{ name: string; age: number }>> {
    return Promise.resolve({
        code: 200,
        message: "success",
        data: { name: "Alice", age: 30 }
    });
}

function getPosts(): Promise<ApiResponse<{ title: string }[]>> {
    return Promise.resolve({
        code: 200,
        message: "success",
        data: [{ title: "TS is awesome" }]
    });
}

// 高级工具:提取 API 函数 Promise 解析后的 Data 部分
// 逻辑:
// 1. T 必须是一个函数
// 2. 函数返回 Promise<ApiResponse<Data>>
// 3. 提取 Data
type ExtractApiData<T extends (...args: any) => any> = 
    ReturnType<T> extends Promise<ApiResponse<infer Data>> ? Data : never;

// 自动推导出的类型
type UserData = ExtractApiData<typeof getUserInfo>; // { name: string; age: number }
type PostsData = ExtractApiData<typeof getPosts>;   // { title: string }[]

// 通用处理函数,自动保持类型
async function apiHandler<TFunc extends (...args: any) => any>(
    apiFunc: TFunc, 
    ...args: Parameters<TFunc>
): Promise<ExtractApiData<TFunc>> {
    const response = await apiFunc(...args);
    // 这里假设 response 结构符合 ApiResponse
    // 在实际项目中通常会有拦截器处理
    if ((response as any).code === 200) {
        return (response as any).data;
    }
    throw new Error((response as any).message);
}

// 使用示例
async function main() {
    // user 自动被推断为 { name: string; age: number }
    const user = await apiHandler(getUserInfo, "123");
    console.log(user.name);

    // posts 自动被推断为 { title: string }[]
    const posts = await apiHandler(getPosts);
    console.log(posts[0].title);
}

模块三:模板字面量类型(Template Literal Types)

TS 4.1 引入的特性,允许对字符串进行模式匹配和操作。这在处理 CSS 类名、事件名称或路由路径时非常强大。

ts 复制代码
/**
 * 模块三:模板字面量类型
 * 场景:CSS-in-JS、事件系统、国际化键值生成
 */

// 1. 简单的字符串拼接
type Color = "red" | "blue";
type Quantity = "100" | "200";
type ItemCssClass = `bg-${Color}-${Quantity}`; 
// "bg-red-100" | "bg-red-200" | "bg-blue-100" | "bg-blue-200"

// 2. 事件名称生成器
type Entity = "User" | "Product" | "Order";
type Operation = "Create" | "Update" | "Delete";

// 自动生成所有可能的事件名
type EventName = `${Entity}${Operation}`; 
// "UserCreate" | "UserUpdate" | ... | "OrderDelete"

// --- 实战:类型安全的事件总线 (Event Bus) ---

// 定义事件负载映射
type EventPayloads = {
    "user:login": { userId: string; time: number };
    "user:logout": void;
    "modal:open": { title: string; content: string };
    "modal:close": void;
};

// 字符串操作工具类型
type Split<S extends string, D extends string> = 
    string extends S ? string[] :
    S extends '' ? [] :
    S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] : [S];

// 测试 Split
type Parts = Split<"user:login:success", ":">; // ["user", "login", "success"]

class TypedEventBus {
    private listeners: { [K in keyof EventPayloads]?: ((payload: EventPayloads[K]) => void)[] } = {};

    // on 方法:EventName 必须是 EventPayloads 的键,cb 参数自动推导
    on<K extends keyof EventPayloads>(eventName: K, cb: (payload: EventPayloads[K]) => void) {
        if (!this.listeners[eventName]) {
            this.listeners[eventName] = [];
        }
        this.listeners[eventName]!.push(cb);
    }

    // emit 方法:payload 类型必须匹配
    emit<K extends keyof EventPayloads>(eventName: K, payload: EventPayloads[K]) {
        const callbacks = this.listeners[eventName];
        if (callbacks) {
            callbacks.forEach(cb => cb(payload));
        }
    }
}

const bus = new TypedEventBus();

// 类型安全:payload 自动推断为 { userId: string; time: number }
bus.on("user:login", (payload) => {
    console.log(`User ${payload.userId} logged in at ${payload.time}`);
});

// 类型报错演示
// bus.emit("user:login", { userId: 123 }); // Error: Type 'number' is not assignable to type 'string'.
bus.emit("user:login", { userId: "u_001", time: Date.now() }); // OK

// --- 进阶:CSS Padding/Margin 简写类型检查 ---

type SizeUnit = "px" | "em" | "rem" | "%";
type SizeValue = `${number}${SizeUnit}` | "0" | "auto";

// 允许 1 到 4 个值
type CSSPadding = 
    | SizeValue
    | `${SizeValue} ${SizeValue}`
    | `${SizeValue} ${SizeValue} ${SizeValue}`
    | `${SizeValue} ${SizeValue} ${SizeValue} ${SizeValue}`;

function setPadding(el: HTMLElement, padding: CSSPadding) {
    el.style.padding = padding;
}

// setPadding(document.body, "10px"); // OK
// setPadding(document.body, "10px 20px"); // OK
// setPadding(document.body, "10px 20px 0 5%"); // OK
// setPadding(document.body, "10"); // Error: 缺少单位
// setPadding(document.body, "red"); // Error

模块四:装饰器(Decorators)与元编程

虽然装饰器在 ECMAScript 中仍处于演进阶段,但在 TypeScript(尤其是 Angular 和 NestJS 生态)中应用广泛。这里展示基于 TypeScript 实验性装饰器的高级用法。

ts 复制代码
/**
 * 模块四:装饰器与 AOP (面向切面编程)
 * 注意:需要在 tsconfig.json 中开启 "experimentalDecorators": true
 */

// 1. 方法装饰器:日志记录与性能监控
function LogPerformance(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = async function (...args: any[]) {
        const start = performance.now();
        console.log(`[Starting] ${propertyKey} with args:`, JSON.stringify(args));
        
        try {
            const result = await originalMethod.apply(this, args);
            const end = performance.now();
            console.log(`[Finished] ${propertyKey} in ${(end - start).toFixed(2)}ms`);
            return result;
        } catch (error) {
            console.error(`[Error] ${propertyKey} failed:`, error);
            throw error;
        }
    };
    return descriptor;
}

// 2. 参数装饰器与方法装饰器配合:参数校验
import "reflect-metadata"; // 需要 npm install reflect-metadata

const REQUIRED_METADATA_KEY = Symbol("required");

// 参数装饰器:标记哪个参数是必填的
function Required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
    let existingRequiredParameters: number[] = Reflect.getOwnMetadata(REQUIRED_METADATA_KEY, target, propertyKey) || [];
    existingRequiredParameters.push(parameterIndex);
    Reflect.defineMetadata(REQUIRED_METADATA_KEY, existingRequiredParameters, target, propertyKey);
}

// 方法装饰器:执行校验逻辑
function Validate(target: any, propertyName: string, descriptor: PropertyDescriptor) {
    let method = descriptor.value;
    descriptor.value = function () {
        let requiredParameters: number[] = Reflect.getOwnMetadata(REQUIRED_METADATA_KEY, target, propertyName);
        if (requiredParameters) {
            for (let parameterIndex of requiredParameters) {
                if (parameterIndex >= arguments.length || arguments[parameterIndex] === undefined || arguments[parameterIndex] === null) {
                    throw new Error(`Missing required argument at index ${parameterIndex} for method ${propertyName}`);
                }
            }
        }
        return method.apply(this, arguments);
    }
}

// 3. 类装饰器:依赖注入 (简易版 IOC 容器)
type Constructor<T = any> = new (...args: any[]) => T;

const ServiceMap = new Map<string, any>();

function Service(name: string) {
    return function(constructor: Constructor) {
        console.log(`Registering service: ${name}`);
        ServiceMap.set(name, new constructor());
    }
}

function Inject(serviceName: string) {
    return function(target: any, propertyKey: string) {
        // 在属性被访问时懒加载注入
        Object.defineProperty(target, propertyKey, {
            get: () => {
                const service = ServiceMap.get(serviceName);
                if (!service) throw new Error(`Service ${serviceName} not found`);
                return service;
            },
            enumerable: true,
            configurable: true
        });
    }
}

// --- 综合应用 ---

@Service("Logger")
class LoggerService {
    log(msg: string) { console.log(`[LOG SERVICE]: ${msg}`); }
}

class UserController {
    @Inject("Logger")
    private logger!: LoggerService;

    @Validate
    @LogPerformance
    async createUser(@Required name: string, @Required email: string, age?: number) {
        // 模拟异步操作
        await new Promise(resolve => setTimeout(resolve, 100));
        
        this.logger.log(`Creating user: ${name} (${email})`);
        return { id: Math.floor(Math.random() * 1000), name, email };
    }
}

// 测试运行
async function runDecorators() {
    const controller = new UserController();
    
    try {
        await controller.createUser("Bob", "bob@example.com"); // 成功
        // await controller.createUser("Alice", null as any); // 抛出 Error: Missing required argument
    } catch (e) {
        console.error(e);
    }
}
// runDecorators();

模块五:React 高级模式与多态组件

在前端开发中,React 的类型定义尤为重要。我们将构建一个多态组件(Polymorphic Component),它可以根据 as 属性改变渲染的 HTML 标签,同时保持类型安全。

ts 复制代码
/**
 * 模块五:React 高级类型模式
 * 场景:组件库开发、高阶组件 (HOC)
 */

import React from 'react';

// 1. 多态组件 (Polymorphic Components)
// 这是一个非常高级的模式,允许 <Text as="h1"> 或 <Text as="a" href="...">

// 获取元素自身的 Props,排除我们要重写的 'as'
type AsProp<C extends React.ElementType> = {
    as?: C;
};

type PropsToOmit<C extends React.ElementType, P> = keyof (AsProp<C> & P);

// 这是一个复杂的类型定义:
// 1. 接收泛型 C (组件类型) 和 Props
// 2. 合并传入的 Props 和 as 属性
// 3. 合并 HTML 原生属性 (Omit 掉重复的)
type PolymorphicComponentProps<C extends React.ElementType, Props = {}> = 
    React.PropsWithChildren<Props & AsProp<C>> &
    Omit<React.ComponentPropsWithoutRef<C>, PropsToOmit<C, Props>>;

// 定义组件类型
type PolymorphicRef<C extends React.ElementType> = React.ComponentPropsWithRef<C>["ref"];

type PolymorphicComponentPropWithRef<C extends React.ElementType, Props = {}> = 
    PolymorphicComponentProps<C, Props> & { ref?: PolymorphicRef<C> };

// 实现组件
type TextProps = {
    color?: 'primary' | 'secondary' | 'danger';
    size?: 'sm' | 'md' | 'lg';
};

// 使用 React.forwardRef 实现
// 注意:由于泛型组件和 forwardRef 的结合在 TS 中比较棘手,通常需要类型断言
export const Text = React.forwardRef(
    <C extends React.ElementType = "span">(
        { as, color = 'primary', size = 'md', children, ...rest }: PolymorphicComponentProps<C, TextProps>,
        ref: PolymorphicRef<C>
    ) => {
        const Component = as || "span";
        
        const style = {
            color: color === 'danger' ? 'red' : 'black',
            fontSize: size === 'lg' ? '20px' : '14px'
        };

        return (
            <Component ref={ref} style={style} {...rest}>
                {children}
            </Component>
        );
    }
) as <C extends React.ElementType = "span">(
    props: PolymorphicComponentPropWithRef<C, TextProps>
) => React.ReactElement | null;

// --- 使用示例 ---

function App() {
    return (
        <div>
            {/* 渲染为 span (默认) */}
            <Text>Hello World</Text>
            
            {/* 渲染为 h1 */}
            <Text as="h1" size="lg">Title</Text>
            
            {/* 渲染为 a 标签,TS 会自动提示 href 属性是必须的/可用的 */}
            <Text as="a" href="https://google.com" color="danger">
                Link
            </Text>
            
            {/* 错误演示:div 没有 href 属性 */}
            {/* <Text as="div" href="..." /> // Error: Property 'href' does not exist on type... */}
        </div>
    );
}

// 2. 类型安全的 Render Props
interface ListProps<T> {
    items: T[];
    renderItem: (item: T, index: number) => React.ReactNode;
}

// 使用泛型组件定义
function List<T>({ items, renderItem }: ListProps<T>) {
    return (
        <ul>
            {items.map((item, index) => (
                <li key={index}>{renderItem(item, index)}</li>
            ))}
        </ul>
    );
}

// 使用
// <List 
//   items={[{ id: 1, name: 'A' }, { id: 2, name: 'B' }]} 
//   renderItem={(item) => <span>{item.name}</span>} // item 自动推断为对象
// />

模块六:混合(Mixins)与类的组合

TypeScript 支持 Mixin 模式,允许我们将多个类的功能组合到一个类中。这在构建复杂的 UI 组件库(如 Material CDK)时非常常见。

ts 复制代码
/**
 * 模块六:Mixins
 * 场景:多重继承模拟、功能组合
 */

// 定义构造函数类型
type Constructor<T = {}> = new (...args: any[]) => T;

// 1. Activatable Mixin: 添加激活/未激活状态
function Activatable<TBase extends Constructor>(Base: TBase) {
    return class extends Base {
        isActivated: boolean = false;

        activate() {
            this.isActivated = true;
            console.log("Activated!");
        }

        deactivate() {
            this.isActivated = false;
            console.log("Deactivated!");
        }
    };
}

// 2. Disposable Mixin: 添加资源清理功能
function Disposable<TBase extends Constructor>(Base: TBase) {
    return class extends Base {
        private disposables: (() => void)[] = [];

        addDisposable(cb: () => void) {
            this.disposables.push(cb);
        }

        dispose() {
            this.disposables.forEach(cb => cb());
            this.disposables = [];
            console.log("Resources disposed.");
        }
    };
}

// 3. Timestamped Mixin: 记录创建时间
function Timestamped<TBase extends Constructor>(Base: TBase) {
    return class extends Base {
        timestamp = new Date();
    };
}

// --- 组合使用 ---

class BaseComponent {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    
    render() {
        console.log(`Rendering ${this.name}`);
    }
}

// 创建一个拥有所有能力的类
// 注意:Mixin 的应用顺序会影响类的层级
const SmartComponent = Timestamped(Disposable(Activatable(BaseComponent)));

// 演示
const comp = new SmartComponent("MyButton");

comp.render(); // 来自 BaseComponent
comp.activate(); // 来自 Activatable
console.log(`Created at: ${comp.timestamp}`); // 来自 Timestamped

comp.addDisposable(() => console.log("Cleaning up event listeners..."));
comp.dispose(); // 来自 Disposable

模块七:高级类型守卫与断言

运行时类型检查对于前端应用的健壮性至关重要。

ts 复制代码
/**
 * 模块七:类型守卫 (Type Guards) 与断言
 * 场景:处理联合类型、运行时数据验证
 */

// 1. 区分联合类型 (Discriminated Unions)
interface Square {
    kind: "square";
    size: number;
}

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

interface Circle {
    kind: "circle";
    radius: number;
}

type Shape = Square | Rectangle | Circle;

// 计算面积:利用 switch 的类型收窄
function area(s: Shape): number {
    switch (s.kind) {
        case "square":
            // 在这里 s 被收窄为 Square
            return s.size * s.size;
        case "rectangle":
            // 在这里 s 被收窄为 Rectangle
            return s.width * s.height;
        case "circle":
            return Math.PI * s.radius ** 2;
        default:
            // 穷尽性检查 (Exhaustiveness checking)
            // 如果未来添加了新形状但没处理,这里会报错
            const _exhaustiveCheck: never = s;
            return _exhaustiveCheck;
    }
}

// 2. 用户自定义类型守卫 (User-Defined Type Guards)
// 返回值类型必须是 `arg is Type`

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

function isDate(value: unknown): value is Date {
    return value instanceof Date;
}

// 复杂对象的类型守卫
interface ApiError {
    error: true;
    code: number;
    details: string;
}

interface ApiSuccess<T> {
    error: false;
    data: T;
}

type ApiResult<T> = ApiSuccess<T> | ApiError;

function isSuccess<T>(result: ApiResult<T>): result is ApiSuccess<T> {
    return result.error === false;
}

// 使用
function handleApiResult(result: ApiResult<string>) {
    if (isSuccess(result)) {
        // TS 知道这里是 ApiSuccess,可以安全访问 .data
        console.log(result.data.toUpperCase());
    } else {
        // TS 知道这里是 ApiError
        console.error(`Error ${result.code}: ${result.details}`);
    }
}

// 3. 断言签名 (Assertion Signatures) - TS 3.7+
// 类似于 Node.js 的 assert 模块
function assertIsNumber(val: any): asserts val is number {
    if (typeof val !== "number") {
        throw new Error("Not a number!");
    }
}

function double(input: any) {
    assertIsNumber(input);
    // 此行之后,input 被视为 number
    return input * 2;
}

模块八:类型体操挑战(Type Gymnastics)

为了展示 TS 的极限能力,我们实现一些类似 Lodash 函数的类型版本。

ts 复制代码
/**
 * 模块八:类型体操
 * 场景:库作者、极度复杂的类型推导
 */

// 1. 实现 Join 类型 (Array.join 的类型版)
type Join<T extends any[], U extends string | number> = 
    T extends [infer F, ...infer R]
        ? R['length'] extends 0
            ? `${F & string}`
            : `${F & string}${U}${Join<R, U>}`
        : "";

type Path = Join<['users', 'id', 'posts'], '/'>; // "users/id/posts"

// 2. 实现 DeepValue (根据路径获取深度属性值)
// 类似于 lodash.get(obj, "a.b.c")

type GetPropType<T, P extends string> = 
    P extends keyof T 
        ? T[P] 
        : P extends `${infer K}.${infer R}`
            ? K extends keyof T 
                ? GetPropType<T[K], R>
                : never
            : never;

// 测试 DeepValue
interface Config {
    app: {
        database: {
            host: string;
            port: number;
        }
    }
}

type DBHost = GetPropType<Config, "app.database.host">; // string
type DBPort = GetPropType<Config, "app.database.port">; // number
type Invalid = GetPropType<Config, "app.database.password">; // never

// 3. 元组转对象
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const;

type TupleToObject<T extends readonly any[]> = {
    [P in T[number]]: P;
};

type CarModelObj = TupleToObject<typeof tuple>;
// { tesla: "tesla", "model 3": "model 3", ... }

// 4. 柯里化函数的类型推导 (Currying)
type Curry<P extends any[], R> = 
    (arg: Head<P>) => HasTail<P> extends true ? Curry<Tail<P>, R> : R;

type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
type Tail<T extends any[]> = ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : [];
type HasTail<T extends any[]> = T extends ([] | [any]) ? false : true;

// 这是一个简化的定义,实际柯里化类型非常复杂
declare function curry<P extends any[], R>(f: (...args: P) => R): Curry<P, R>;

const add = (a: number, b: number, c: number) => a + b + c;
const curriedAdd = curry(add);

// const sum = curriedAdd(1)(2)(3); // 理想情况下推导出 number

模块九:声明合并与模块扩展 (Module Augmentation)

在扩展第三方库(如给 Window 对象添加属性,或扩展 React 的 Theme)时必不可少。

ts 复制代码
/**
 * 模块九:模块扩展
 * 场景:全局变量、第三方库补丁
 */

// 1. 扩展全局 Window 对象
declare global {
    interface Window {
        __REDUX_DEVTOOLS_EXTENSION__: any;
        Analytics: {
            track: (event: string) => void;
        };
    }
    
    // 扩展 String 原型
    interface String {
        toTitleCase(): string;
    }
}

// 实现扩展的方法
String.prototype.toTitleCase = function() {
    return this.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
};

// 使用
// window.Analytics.track("Page View");
// const title = "hello world".toTitleCase();

// 2. 扩展第三方库 (以 styled-components 为例)
// 假设这是一个名为 'my-theme-lib' 的库
// import 'my-theme-lib';

// declare module 'my-theme-lib' {
//     export interface DefaultTheme {
//         borderRadius: string;
//         colors: {
//             main: string;
//             secondary: string;
//         };
//     }
// }

总结与最佳实践

建议:

  1. 不要过度设计:虽然泛型和条件类型很强大,但如果一个类型定义超过了 10 行且难以阅读,考虑简化它或添加详细注释。
  2. 利用 unknown 代替 any :在不确定类型时,unknown 强制你在使用前进行类型检查,比 any 安全得多。
  3. 优先使用接口 (Interface) 定义对象:接口支持声明合并,对库的扩展性更好;类型别名 (Type Alias) 更适合联合类型和元组。
  4. 严格模式 :始终开启 strict: true,特别是 strictNullChecks,这能避免 80% 的运行时错误。
相关推荐
少卿4 小时前
React Compiler 完全指南:自动化性能优化的未来
前端·javascript
广州华水科技4 小时前
水库变形监测推荐:2025年单北斗GNSS变形监测系统TOP5,助力基础设施安全
前端
广州华水科技4 小时前
北斗GNSS变形监测一体机在基础设施安全中的应用与优势
前端
七淮4 小时前
umi4暗黑模式设置
前端
8***B4 小时前
前端路由权限控制,动态路由生成
前端
军军3605 小时前
从图片到点阵:用JavaScript重现复古数码点阵艺术图
前端·javascript
znhy@1235 小时前
Vue基础知识(一)
前端·javascript·vue.js
terminal0075 小时前
浅谈useRef的使用和渲染机制
前端·react.js·面试
我的小月月5 小时前
🔥 手把手教你实现前端邮件预览功能
前端·vue.js
陈佬昔没带相机5 小时前
MiniMax M2 + Trae 编码评测:能否与 Claude 4.5 扳手腕?
前端·人工智能·ai编程