TypeScript 5.4 高级实战指南:从入门到进阶

TypeScript 5.4 高级实战指南:从入门到进阶

本文从实际开发角度出发,深入讲解 TypeScript 的高级特性和实战技巧。每个概念都配有详细的代码示例和最佳实践建议。

目录

  1. 基础配置优化
  2. 高级类型系统
  3. 类型体操实战
  4. 工程化实践
  5. 性能优化
  6. 实战案例

基础配置优化

tsconfig.json 最佳实践

{
  "compilerOptions": {
    // 目标 ECMAScript 版本
    "target": "ES2022",
    // 模块系统
    "module": "ESNext",
    // 模块解析策略
    "moduleResolution": "bundler",
    // 严格模式
    "strict": true,
    // 类型检查
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    // 额外检查
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    // 模块导入
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    // JSX 支持
    "jsx": "preserve",
    // 装饰器支持
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    // 路径别名
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

项目结构最佳实践

src/
├── types/              # 类型声明文件
│   ├── global.d.ts    # 全局类型声明
│   └── module.d.ts    # 模块类型声明
├── utils/             # 工具函数
├── services/          # API 服务
├── components/        # 组件
└── pages/            # 页面

高级类型系统

1. 高级类型推断

// 函数参数推断
function map<T, U>(arr: T[], fn: (item: T) => U): U[] {
  return arr.map(fn);
}

// 使用示例
const numbers = [1, 2, 3];
const strings = map(numbers, n => n.toString()); // string[]

// 条件类型推断
type ElementType<T> = T extends (infer U)[] ? U : never;
type StringType = ElementType<string[]>; // string

2. 映射类型高级用法

// 深度 Readonly
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object 
    ? DeepReadonly<T[P]> 
    : T[P];
};

// 可选深度 Partial
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object
    ? DeepPartial<T[P]>
    : T[P];
};

// 类型过滤
type FilterByValueType<T, ValueType> = {
  [P in keyof T as T[P] extends ValueType ? P : never]: T[P];
};

// 使用示例
interface User {
  id: number;
  name: string;
  settings: {
    theme: string;
    notifications: boolean;
  };
}

type ReadonlyUser = DeepReadonly<User>;
type PartialUser = DeepPartial<User>;
type StringPropsOnly = FilterByValueType<User, string>;

3. 高级泛型约束

// 泛型约束
interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(arg: T): number {
  return arg.length;
}

// 泛型默认值
interface DefaultGeneric<T = string> {
  value: T;
}

// 多重泛型约束
function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

// 泛型工厂
interface GenericFactory<T> {
  create(): T;
  update(item: Partial<T>): void;
  delete(id: string): void;
}

类型体操实战

1. 递归类型

// 递归数组类型
type NestedArray<T> = Array<T | NestedArray<T>>;

// 递归对象类型
type NestedObject<T> = {
  [key: string]: T | NestedObject<T>;
};

// JSON 类型
type JSONValue = 
  | string 
  | number 
  | boolean 
  | null 
  | JSONValue[] 
  | { [key: string]: JSONValue };

// 使用示例
const nestedArr: NestedArray<number> = [1, [2, [3, 4]], 5];
const nestedObj: NestedObject<string> = {
  a: "value",
  b: {
    c: "nested value"
  }
};

2. 条件类型进阶

// 联合类型转交叉类型
type UnionToIntersection<U> = 
  (U extends any ? (k: U) => void : never) extends 
  ((k: infer I) => void) ? I : never;

// 提取函数返回类型
type ReturnTypeDeep<T> = T extends (...args: any[]) => infer R
  ? R extends Promise<infer P>
    ? P
    : R
  : never;

// 字符串操作类型
type CamelCase<S extends string> = S extends `${infer P1}_${infer P2}${infer P3}`
  ? `${P1}${Uppercase<P2>}${CamelCase<P3>}`
  : S;

// 使用示例
type Union = { a: string } | { b: number };
type Intersection = UnionToIntersection<Union>; // { a: string } & { b: number }

async function fetchUser() {
  return { id: 1, name: "John" };
}
type UserType = ReturnTypeDeep<typeof fetchUser>; // { id: number, name: string }

type CamelCaseExample = CamelCase<"user_first_name">; // "userFirstName"

3. 实用工具类型

// 移除索引签名
type RemoveIndexSignature<T> = {
  [K in keyof T as string extends K 
    ? never 
    : number extends K 
      ? never 
      : K]: T[K];
};

// 必选属性
type RequiredKeys<T> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];

// 可选属性
type OptionalKeys<T> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];

// 使用示例
interface Test {
  [key: string]: unknown;
  id: number;
  name?: string;
  age: number;
}

type CleanTest = RemoveIndexSignature<Test>;
type Required = RequiredKeys<Test>; // "id" | "age"
type Optional = OptionalKeys<Test>; // "name"

工程化实践

1. 模块声明

// 声明全局变量
declare global {
  interface Window {
    __REDUX_DEVTOOLS_EXTENSION__: any;
  }
}

// 声明模块
declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}

// 声明 JSON 模块
declare module "*.json" {
  const value: any;
  export default value;
}

2. 类型声明文件组织

// types/api.d.ts
declare namespace API {
  interface Response<T = any> {
    code: number;
    data: T;
    message: string;
  }

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

// types/global.d.ts
declare global {
  type Nullable<T> = T | null;
  type Optional<T> = T | undefined;
  type Recordable<T = any> = Record<string, T>;
}

3. 装饰器使用

// 方法装饰器
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function(...args: any[]) {
    console.log(`Calling ${propertyKey} with:`, args);
    const result = originalMethod.apply(this, args);
    console.log(`Result:`, result);
    return result;
  };

  return descriptor;
}

// 类装饰器
function controller(path: string) {
  return function(target: Function) {
    Reflect.defineMetadata('path', path, target);
  };
}

// 使用示例
@controller('/api')
class UserController {
  @log
  getUser(id: number) {
    return { id, name: 'John' };
  }
}

性能优化

1. 类型缓存

// 使用接口合并而不是交叉类型
// 好的做法
interface MergedType extends Type1, Type2 {}

// 避免的做法
type MergedType = Type1 & Type2;

// 使用类型参数约束
function getValue<T extends object, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

2. 条件类型优化

// 避免过深的条件类型嵌套
type Flatten<T> = T extends Array<infer U> ? U : T;

// 使用 never 过滤类型
type NonNullable<T> = T extends null | undefined ? never : T;

// 使用 unknown 而不是 any
function processValue(value: unknown) {
  if (typeof value === 'string') {
    return value.toUpperCase();
  }
  return String(value);
}

实战案例

1. Vue 3 + TypeScript

// 组件 Props 类型定义
interface Props {
  title: string;
  items?: string[];
  onSelect?: (item: string) => void;
}

// 组件实现
import { defineComponent, PropType } from 'vue';

export default defineComponent({
  name: 'MyComponent',
  props: {
    title: {
      type: String,
      required: true
    },
    items: {
      type: Array as PropType<string[]>,
      default: () => []
    },
    onSelect: {
      type: Function as PropType<(item: string) => void>,
      default: undefined
    }
  },
  setup(props) {
    // 组件逻辑
  }
});

2. React + TypeScript

// 组件 Props 类型定义
interface Props {
  title: string;
  count: number;
  children?: React.ReactNode;
  onIncrement?: () => void;
}

// 组件实现
const Counter: React.FC<Props> = ({ 
  title, 
  count, 
  children, 
  onIncrement 
}) => {
  return (
    <div>
      <h2>{title}</h2>
      <p>Count: {count}</p>
      {children}
      <button onClick={onIncrement}>Increment</button>
    </div>
  );
};

// 自定义 Hook
function useCounter(initialValue: number = 0) {
  const [count, setCount] = useState(initialValue);

  const increment = useCallback(() => {
    setCount(prev => prev + 1);
  }, []);

  return { count, increment };
}

3. Express + TypeScript

import express from 'express';
import { Request, Response, NextFunction } from 'express';

// 自定义请求类型
interface AuthRequest extends Request {
  user?: {
    id: string;
    role: string;
  };
}

// 中间件类型
type Middleware = (
  req: AuthRequest,
  res: Response,
  next: NextFunction
) => Promise<void> | void;

// 错误处理
interface AppError extends Error {
  statusCode?: number;
  status?: string;
  isOperational?: boolean;
}

// 错误处理中间件
const errorHandler = (
  err: AppError,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  err.statusCode = err.statusCode || 500;
  err.status = err.status || 'error';

  res.status(err.statusCode).json({
    status: err.status,
    message: err.message
  });
};

// 路由处理
const app = express();

app.get('/api/users/:id', async (req: AuthRequest, res: Response) => {
  try {
    const userId = req.params.id;
    // 处理逻辑
    res.json({ /* user data */ });
  } catch (error) {
    next(error);
  }
});

app.use(errorHandler);

调试技巧

1. 类型检查

// 类型检查工具
type TypeEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? true : false;

// 检查是否是 never 类型
type IsNever<T> = [T] extends [never] ? true : false;

// 类型断言
function assertIsString(val: unknown): asserts val is string {
  if (typeof val !== "string") {
    throw new Error("Not a string!");
  }
}

2. VS Code 调试配置

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug TypeScript",
      "program": "${workspaceFolder}/src/index.ts",
      "preLaunchTask": "tsc: build - tsconfig.json",
      "outFiles": ["${workspaceFolder}/dist/**/*.js"]
    }
  ]
}

最佳实践总结

  1. 类型定义原则

    • 优先使用接口定义对象类型
    • 使用类型别名定义函数和联合类型
    • 合理使用泛型提高代码复用性
  2. 代码组织

    • 按功能模块组织类型定义
    • 使用命名空间避免命名冲突
    • 合理使用类型导入导出
  3. 性能优化

    • 避免过度使用条件类型
    • 合理使用类型缓存
    • 控制类型递归深度

常见问题解决方案

  1. 类型扩展
// 扩展已有类型
interface Window {
  __REDUX_DEVTOOLS_EXTENSION__: any;
}

// 扩展第三方模块
declare module 'vue' {
  interface ComponentCustomProperties {
    $http: typeof axios;
  }
}
  1. 类型保护
// 自定义类型保护
function isError(error: unknown): error is Error {
  return error instanceof Error;
}

// 使用类型谓词
function isString(value: unknown): value is string {
  return typeof value === 'string';
}
  1. 类型兼容性
// 结构类型系统
interface Point2D {
  x: number;
  y: number;
}

interface Point3D {
  x: number;
  y: number;
  z: number;
}

let point2D: Point2D = { x: 0, y: 0 };
let point3D: Point3D = { x: 0, y: 0, z: 0 };

// Point2D 兼容 Point3D
point2D = point3D; // OK

结语

TypeScript 是一个强大的工具,掌握它的高级特性可以帮助我们写出更加健壮和可维护的代码。希望这篇指南能帮助你更好地理解和使用 TypeScript。

记住:类型系统是用来服务我们的,而不是限制我们。在实际开发中,要根据具体场景选择合适的类型策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值