name: typescript description: TypeScript语言专长,用于编写类型安全、生产质量的TypeScript代码。适用于TypeScript开发、高级类型系统特性、严格模式、类型安全API和现代框架。触发词:typescript, ts, tsx, type, interface, generic, union, intersection, discriminated union, type guard, type assertion, utility types, conditional types, mapped types, zod, trpc, prisma, react, node, nodejs, deno, bun, npm, pnpm, yarn, type-safe, type safety, tsconfig, strict mode, branded types
TypeScript语言专长
概述
此技能提供编写类型安全、可维护和生产质量的TypeScript代码的指导。它覆盖了TypeScript的高级类型系统特性、严格模式配置、模块系统和常见设计模式。
关键概念
泛型
// 基本泛型
function identity<T>(value: T): T {
return value;
}
// 多个类型参数
function map<T, U>(items: T[], fn: (item: T) => U): U[] {
return items.map(fn);
}
// 泛型约束
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): void {
console.log(item.length);
}
// 泛型类
class Repository<T extends { id: string }> {
private items: Map<string, T> = new Map();
save(item: T): void {
this.items.set(item.id, item);
}
findById(id: string): T | undefined {
return this.items.get(id);
}
findAll(): T[] {
return Array.from(this.items.values());
}
}
// 默认类型参数
interface ApiResponse<T = unknown> {
data: T;
status: number;
message: string;
}
实用类型
// 内置实用类型
interface User {
id: string;
email: string;
name: string;
role: "admin" | "user";
createdAt: Date;
}
// Partial - 所有属性可选
type UserUpdate = Partial<User>;
// Required - 所有属性必需
type RequiredUser = Required<User>;
// Readonly - 所有属性只读
type ImmutableUser = Readonly<User>;
// Pick - 选择特定属性
type UserCredentials = Pick<User, "email" | "id">;
// Omit - 排除特定属性
type UserWithoutDates = Omit<User, "createdAt">;
// Record - 创建具有特定键的对象类型
type UserRoles = Record<string, "admin" | "user" | "guest">;
// Extract/Exclude 用于联合类型
type StringOrNumber = string | number | boolean;
type OnlyStrings = Extract<StringOrNumber, string>; // string
type NoStrings = Exclude<StringOrNumber, string>; // number | boolean
// ReturnType 和 Parameters
function createUser(name: string, email: string): User {
return {
id: crypto.randomUUID(),
name,
email,
role: "user",
createdAt: new Date(),
};
}
type CreateUserReturn = ReturnType<typeof createUser>; // User
type CreateUserParams = Parameters<typeof createUser>; // [string, string]
// NonNullable
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>; // string
条件类型
// 基本条件类型
type IsString<T> = T extends string ? true : false;
// Infer 关键字用于类型提取
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type UnwrapArray<T> = T extends (infer U)[] ? U : T;
// 嵌套推断
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 分配条件类型
type ToArray<T> = T extends any ? T[] : never;
type StringOrNumberArray = ToArray<string | number>; // string[] | number[]
// 非分配条件类型
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type Combined = ToArrayNonDist<string | number>; // (string | number)[]
// 实际示例:提取函数参数
type FirstParameter<T> = T extends (first: infer F, ...args: any[]) => any
? F
: never;
映射类型
// 基本映射类型
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
// 带修饰符
type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};
type Optional<T> = {
[K in keyof T]+?: T[K];
};
// 键重映射 (TypeScript 4.1+)
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type Setters<T> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};
// 过滤键
type FilterByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
interface Mixed {
name: string;
age: number;
active: boolean;
email: string;
}
type StringProps = FilterByType<Mixed, string>; // { name: string; email: string }
// 实际:API响应转换
type ApiDTO<T> = {
[K in keyof T as `${string & K}DTO`]: T[K] extends Date ? string : T[K];
};
可辨识联合
// 定义带字面类型判别器的可辨识联合
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function handleResult<T>(result: Result<T>): T {
if (result.success) {
return result.data; // TypeScript在此处知道data存在
}
throw result.error; // TypeScript在此处知道error存在
}
// 更复杂示例:状态机
type LoadingState =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: User[] }
| { status: "error"; error: Error };
function renderState(state: LoadingState): string {
switch (state.status) {
case "idle":
return "点击加载";
case "loading":
return "加载中...";
case "success":
return `已加载 ${state.data.length} 个用户`;
case "error":
return `错误: ${state.error.message}`;
}
}
// Redux风格reducer的动作类型
type Action =
| { type: "SET_USER"; payload: User }
| { type: "CLEAR_USER" }
| { type: "SET_ERROR"; payload: string };
function reducer(state: State, action: Action): State {
switch (action.type) {
case "SET_USER":
return { ...state, user: action.payload };
case "CLEAR_USER":
return { ...state, user: null };
case "SET_ERROR":
return { ...state, error: action.payload };
}
}
类型守卫
// typeof 守卫
function process(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase();
}
return value.toFixed(2);
}
// instanceof 守卫
function handleError(error: unknown): string {
if (error instanceof Error) {
return error.message;
}
return String(error);
}
// 自定义类型守卫
interface Cat {
meow(): void;
}
interface Dog {
bark(): void;
}
function isCat(animal: Cat | Dog): animal is Cat {
return "meow" in animal;
}
// 带可辨识联合的类型守卫
function isSuccess<T>(result: Result<T>): result is { success: true; data: T } {
return result.success;
}
// 断言函数
function assertNonNull<T>(
value: T | null | undefined,
message?: string,
): asserts value is T {
if (value === null || value === undefined) {
throw new Error(message ?? "值为空或未定义");
}
}
// 用法
function processUser(user: User | null) {
assertNonNull(user, "用户必须存在");
// user现在为User类型(非空)
console.log(user.name);
}
最佳实践
严格模式配置
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
模块组织
// 重新导出模式以提供干净的公共API
// src/models/index.ts
export { User, type UserDTO } from "./user";
export { Order, type OrderDTO } from "./order";
export { Product, type ProductDTO } from "./product";
// 带显式类型的桶导出
// src/index.ts
export type { Config, ConfigOptions } from "./config";
export { createConfig, validateConfig } from "./config";
// 相关工具的命名空间导入
import * as validators from "./validators";
import * as formatters from "./formatters";
// 仅类型导入
import type { User, Order } from "./models";
import { createUser, createOrder } from "./models";
声明文件
// global.d.ts - 扩展全局类型
declare global {
interface Window {
analytics: AnalyticsAPI;
}
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: "development" | "production" | "test";
DATABASE_URL: string;
API_KEY: string;
}
}
}
// module.d.ts - 声明未类型化的模块
declare module "untyped-package" {
export function doSomething(value: string): void;
export const VERSION: string;
}
// 增强现有模块
declare module "express" {
interface Request {
user?: User;
requestId: string;
}
}
export {}; // 使其成为模块
常见模式
品牌类型
// 创建名义类型以确保类型安全
declare const brand: unique symbol;
type Brand<T, B> = T & { [brand]: B };
type UserId = Brand<string, "UserId">;
type OrderId = Brand<string, "OrderId">;
type Email = Brand<string, "Email">;
// 带验证的构造函数
function createUserId(id: string): UserId {
if (!id.match(/^usr_[a-z0-9]+$/)) {
throw new Error("无效的用户ID格式");
}
return id as UserId;
}
function createEmail(email: string): Email {
if (!email.includes("@")) {
throw new Error("无效的电子邮件格式");
}
return email.toLowerCase() as Email;
}
// 现在这些不能意外混合
function getUser(id: UserId): Promise<User> {
/* ... */
}
function getOrder(id: OrderId): Promise<Order> {
/* ... */
}
// const userId = createUserId('usr_123');
// const orderId = createOrderId('ord_456');
// getUser(orderId); // 类型错误!
构建器模式
class QueryBuilder<T extends object> {
private filters: Partial<T> = {};
private sortField?: keyof T;
private sortOrder: "asc" | "desc" = "asc";
private limitValue?: number;
private offsetValue?: number;
where<K extends keyof T>(field: K, value: T[K]): this {
this.filters[field] = value;
return this;
}
orderBy(field: keyof T, order: "asc" | "desc" = "asc"): this {
this.sortField = field;
this.sortOrder = order;
return this;
}
limit(value: number): this {
this.limitValue = value;
return this;
}
offset(value: number): this {
this.offsetValue = value;
return this;
}
build(): Query<T> {
return {
filters: this.filters,
sort: this.sortField
? { field: this.sortField, order: this.sortOrder }
: undefined,
pagination: { limit: this.limitValue, offset: this.offsetValue },
};
}
}
// 带类型推断的用法
const query = new QueryBuilder<User>()
.where("role", "admin")
.orderBy("createdAt", "desc")
.limit(10)
.build();
穷举检查
// 确保处理所有联合情况
function assertNever(value: never): never {
throw new Error(`意外值: ${value}`);
}
type Status = "pending" | "approved" | "rejected" | "cancelled";
function getStatusColor(status: Status): string {
switch (status) {
case "pending":
return "yellow";
case "approved":
return "green";
case "rejected":
return "red";
case "cancelled":
return "gray";
default:
return assertNever(status); // 如果缺少情况,编译错误
}
}
// 带可辨识联合
type Event =
| { type: "click"; x: number; y: number }
| { type: "keypress"; key: string }
| { type: "scroll"; delta: number };
function handleEvent(event: Event): void {
switch (event.type) {
case "click":
console.log(`点击于 ${event.x}, ${event.y}`);
break;
case "keypress":
console.log(`按键: ${event.key}`);
break;
case "scroll":
console.log(`滚动: ${event.delta}`);
break;
default:
assertNever(event);
}
}
类型安全事件发射器
type EventMap = {
userCreated: { user: User };
userDeleted: { userId: string };
orderPlaced: { order: Order; user: User };
};
class TypedEventEmitter<T extends Record<string, any>> {
private listeners: { [K in keyof T]?: Array<(payload: T[K]) => void> } = {};
on<K extends keyof T>(
event: K,
listener: (payload: T[K]) => void,
): () => void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(listener);
return () => this.off(event, listener);
}
off<K extends keyof T>(event: K, listener: (payload: T[K]) => void): void {
const listeners = this.listeners[event];
if (listeners) {
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
}
}
emit<K extends keyof T>(event: K, payload: T[K]): void {
this.listeners[event]?.forEach((listener) => listener(payload));
}
}
// 用法
const emitter = new TypedEventEmitter<EventMap>();
emitter.on("userCreated", ({ user }) => {
console.log(`用户创建: ${user.name}`);
});
emitter.emit("userCreated", { user: newUser });
// emitter.emit('userCreated', { wrong: 'payload' }); // 类型错误!
类型安全API模式
Zod用于运行时验证
import { z } from "zod";
// 定义生成运行时验证器和静态类型的模式
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1).max(100),
age: z.number().int().positive().optional(),
role: z.enum(["admin", "user", "guest"]).default("user"),
createdAt: z.coerce.date(),
metadata: z.record(z.string(), z.unknown()),
});
// 从模式提取TypeScript类型
type User = z.infer<typeof UserSchema>;
// 嵌套模式
const OrderSchema = z.object({
id: z.string(),
user: UserSchema,
items: z.array(
z.object({
productId: z.string(),
quantity: z.number().positive(),
price: z.number().positive(),
}),
),
total: z.number().positive(),
status: z.enum(["pending", "paid", "shipped", "delivered"]),
});
type Order = z.infer<typeof OrderSchema>;
// 带错误处理的解析
function createUser(input: unknown): User {
return UserSchema.parse(input); // 验证失败时抛出ZodError
}
// 安全解析返回结果对象
function createUserSafe(input: unknown): Result<User, z.ZodError> {
const result = UserSchema.safeParse(input);
if (result.success) {
return { success: true, data: result.data };
}
return { success: false, error: result.error };
}
// 转换和精炼
const PasswordSchema = z
.string()
.min(8)
.regex(/[A-Z]/, "必须包含大写字母")
.regex(/[a-z]/, "必须包含小写字母")
.regex(/[0-9]/, "必须包含数字");
const SignupSchema = z
.object({
email: z.string().email(),
password: PasswordSchema,
confirmPassword: z.string(),
})
.refine((data) => data.password === data.confirmPassword, {
message: "密码必须匹配",
path: ["confirmPassword"],
});
// 模式上的partial, pick, omit
const UserUpdateSchema = UserSchema.partial(); // 所有字段可选
const UserCredentialsSchema = UserSchema.pick({ email: true, id: true });
const UserWithoutDatesSchema = UserSchema.omit({ createdAt: true });
tRPC用于端到端类型安全
import { initTRPC } from "@trpc/server";
import { z } from "zod";
// 初始化tRPC
const t = initTRPC.context<Context>().create();
// 定义带类型过程的router
const appRouter = t.router({
// 带输入验证的查询
getUser: t.procedure
.input(z.object({ id: z.string().uuid() }))
.query(async ({ input, ctx }) => {
const user = await ctx.db.user.findUnique({
where: { id: input.id },
});
if (!user) throw new TRPCError({ code: "NOT_FOUND" });
return user;
}),
// 带输入验证的突变
createUser: t.procedure
.input(UserSchema.omit({ id: true, createdAt: true }))
.mutation(async ({ input, ctx }) => {
return await ctx.db.user.create({ data: input });
}),
// 带中间件的受保护过程
updateProfile: t.procedure
.use(isAuthenticated)
.input(UserSchema.partial().required({ id: true }))
.mutation(async ({ input, ctx }) => {
return await ctx.db.user.update({
where: { id: input.id },
data: input,
});
}),
// 嵌套router
posts: t.router({
list: t.procedure
.input(
z.object({
limit: z.number().min(1).max(100).default(10),
cursor: z.string().optional(),
})
)
.query(async ({ input }) => {
// 返回类型化数据
return { posts: [], nextCursor: null };
}),
byId: t.procedure.input(z.string()).query(async ({ input }) => {
// input为字符串
return { id: input, title: "帖子" };
}),
}),
});
// 导出类型供客户端使用
export type AppRouter = typeof appRouter;
// 客户端用法(在单独文件中)
import { createTRPCClient } from "@trpc/client";
import type { AppRouter } from "./server";
const client = createTRPCClient<AppRouter>({
url: "http://localhost:3000/trpc",
});
// 完全类型化,自动完成有效
const user = await client.getUser.query({ id: "uuid-here" });
// user类型为User
const newUser = await client.createUser.mutate({
email: "user@example.com",
name: "John",
role: "user",
});
// newUser基于突变返回类型化
// React钩子用法
import { trpc } from "./trpc";
function UserProfile({ userId }: { userId: string }) {
const { data, isLoading } = trpc.getUser.useQuery({ id: userId });
const updateMutation = trpc.updateProfile.useMutation();
if (isLoading) return <div>加载中...</div>;
return <div>{data.name}</div>;
}
Prisma用于类型安全数据库访问
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
// 从schema.prisma生成的类型
// 所有查询完全类型化
// 基本CRUD操作
async function createUser(email: string, name: string) {
return await prisma.user.create({
data: { email, name },
// select/include类型检查
select: { id: true, email: true, name: true },
});
}
// 关系类型化
async function getUserWithPosts(userId: string) {
return await prisma.user.findUnique({
where: { id: userId },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: "desc" },
take: 10,
},
},
});
// 返回类型包括User & { posts: Post[] }
}
// 类型安全的where子句
async function findUsers(filters: {
role?: string;
createdAfter?: Date;
emailContains?: string;
}) {
return await prisma.user.findMany({
where: {
role: filters.role,
createdAt: { gte: filters.createdAfter },
email: { contains: filters.emailContains },
},
});
}
// 事务
async function transferCredits(fromId: string, toId: string, amount: number) {
return await prisma.$transaction(async (tx) => {
const from = await tx.user.update({
where: { id: fromId },
data: { credits: { decrement: amount } },
});
const to = await tx.user.update({
where: { id: toId },
data: { credits: { increment: amount } },
});
return { from, to };
});
}
// 扩展Prisma Client添加自定义方法
const xprisma = prisma.$extends({
model: {
user: {
async findByEmail(email: string) {
return await prisma.user.findUnique({ where: { email } });
},
},
},
});
React TypeScript模式
组件Props和泛型组件
import { ReactNode, ComponentPropsWithoutRef } from "react";
// 带props接口的基本组件
interface ButtonProps {
variant: "primary" | "secondary" | "danger";
size?: "sm" | "md" | "lg";
disabled?: boolean;
onClick?: () => void;
children: ReactNode;
}
function Button({
variant,
size = "md",
disabled,
onClick,
children,
}: ButtonProps) {
return (
<button
className={`btn-${variant} btn-${size}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
}
// 扩展原生HTML元素props
interface InputProps extends ComponentPropsWithoutRef<"input"> {
label: string;
error?: string;
}
function Input({ label, error, ...inputProps }: InputProps) {
return (
<div>
<label>{label}</label>
<input {...inputProps} aria-invalid={!!error} />
{error && <span className="error">{error}</span>}
</div>
);
}
// 列表的泛型组件
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => ReactNode;
keyExtractor: (item: T) => string | number;
emptyMessage?: string;
}
function List<T>({
items,
renderItem,
keyExtractor,
emptyMessage,
}: ListProps<T>) {
if (items.length === 0) {
return <div>{emptyMessage || "无项目"}</div>;
}
return (
<ul>
{items.map((item, index) => (
<li key={keyExtractor(item)}>{renderItem(item, index)}</li>
))}
</ul>
);
}
// 带类型推断的用法
<List
items={users}
renderItem={(user) => <div>{user.name}</div>}
keyExtractor={(user) => user.id}
/>;
// 多态组件(as prop模式)
type AsProp<C extends React.ElementType> = {
as?: C;
};
type PropsToOmit<C extends React.ElementType, P> = keyof (AsProp<C> & P);
type PolymorphicComponentProp<
C extends React.ElementType,
Props = {}
> = React.PropsWithChildren<Props & AsProp<C>> &
Omit<React.ComponentPropsWithoutRef<C>, PropsToOmit<C, Props>>;
type TextProps<C extends React.ElementType> = PolymorphicComponentProp<
C,
{
color?: "primary" | "secondary";
size?: "sm" | "md" | "lg";
}
>;
function Text<C extends React.ElementType = "span">({
as,
color = "primary",
size = "md",
children,
...props
}: TextProps<C>) {
const Component = as || "span";
return (
<Component className={`text-${color} text-${size}`} {...props}>
{children}
</Component>
);
}
// 用法
<Text>默认span</Text>;
<Text as="h1">标题</Text>;
<Text as="a" href="/link">
链接
</Text>;
钩子和状态管理
import { useState, useEffect, useCallback, useRef, useReducer } from "react";
// 类型化useState
function Counter() {
const [count, setCount] = useState(0);
const [user, setUser] = useState<User | null>(null);
// 类型推断有效
setCount(count + 1);
setUser({ id: "1", name: "John", email: "john@example.com" });
}
// 带泛型类型的自定义钩子
function useLocalStorage<T>(key: string, initialValue: T) {
const [value, setValue] = useState<T>(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue] as const;
}
// 带类型推断的用法
const [user, setUser] = useLocalStorage<User | null>("user", null);
// 带可辨识联合的useReducer
type State = {
status: "idle" | "loading" | "success" | "error";
data: User | null;
error: string | null;
};
type Action =
| { type: "FETCH_START" }
| { type: "FETCH_SUCCESS"; payload: User }
| { type: "FETCH_ERROR"; payload: string };
function reducer(state: State, action: Action): State {
switch (action.type) {
case "FETCH_START":
return { status: "loading", data: null, error: null };
case "FETCH_SUCCESS":
return { status: "success", data: action.payload, error: null };
case "FETCH_ERROR":
return { status: "error", data: null, error: action.payload };
}
}
function useUser(userId: string) {
const [state, dispatch] = useReducer(reducer, {
status: "idle",
data: null,
error: null,
});
useEffect(() => {
dispatch({ type: "FETCH_START" });
fetchUser(userId)
.then((user) => dispatch({ type: "FETCH_SUCCESS", payload: user }))
.catch((error) =>
dispatch({ type: "FETCH_ERROR", payload: error.message })
);
}, [userId]);
return state;
}
// 带类型化DOM元素的Ref
function VideoPlayer() {
const videoRef = useRef<HTMLVideoElement>(null);
const play = useCallback(() => {
videoRef.current?.play();
}, []);
return <video ref={videoRef} />;
}
带TypeScript的Context API
import { createContext, useContext, ReactNode } from "react";
// 定义context值类型
interface AuthContextValue {
user: User | null;
login: (email: string, password: string) => Promise<void>;
logout: () => Promise<void>;
isLoading: boolean;
}
// 创建带未定义初始值的context
const AuthContext = createContext<AuthContextValue | undefined>(undefined);
// 提供者组件
interface AuthProviderProps {
children: ReactNode;
}
function AuthProvider({ children }: AuthProviderProps) {
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(false);
const login = async (email: string, password: string) => {
setIsLoading(true);
const user = await api.login(email, password);
setUser(user);
setIsLoading(false);
};
const logout = async () => {
await api.logout();
setUser(null);
};
const value = { user, login, logout, isLoading };
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
// 带运行时检查的自定义钩子
function useAuth(): AuthContextValue {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error("useAuth必须在AuthProvider内使用");
}
return context;
}
// 在组件中的用法
function Profile() {
const { user, logout } = useAuth(); // 完全类型化
if (!user) return <div>未登录</div>;
return (
<div>
<h1>{user.name}</h1>
<button onClick={logout}>登出</button>
</div>
);
}
Node.js TypeScript模式
带类型安全的Express
import express, { Request, Response, NextFunction } from "express";
import { z } from "zod";
// 扩展Express类型
declare global {
namespace Express {
interface Request {
user?: User;
}
}
}
// 类型安全请求处理程序
interface TypedRequest<
TBody = unknown,
TQuery = unknown,
TParams = unknown,
> extends Request {
body: TBody;
query: TQuery;
params: TParams;
}
interface TypedResponse<TData = unknown> extends Response {
json: (data: TData) => this;
}
// 验证中间件工厂
function validate<T>(schema: z.ZodSchema<T>) {
return (req: Request, res: Response, next: NextFunction) => {
try {
req.body = schema.parse(req.body);
next();
} catch (error) {
if (error instanceof z.ZodError) {
res.status(400).json({ errors: error.errors });
} else {
next(error);
}
}
};
}
// 类型化路由处理程序
type RouteHandler<
TBody = unknown,
TQuery = unknown,
TParams = unknown,
TData = unknown,
> = (
req: TypedRequest<TBody, TQuery, TParams>,
res: TypedResponse<TData>,
next: NextFunction,
) => void | Promise<void>;
// 示例用法
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string(),
age: z.number().optional(),
});
type CreateUserBody = z.infer<typeof CreateUserSchema>;
type CreateUserResponse = { user: User };
const createUserHandler: RouteHandler<
CreateUserBody,
{},
{},
CreateUserResponse
> = async (req, res) => {
const user = await db.createUser(req.body);
res.json({ user });
};
const app = express();
app.post("/users", validate(CreateUserSchema), createUserHandler);
// 带可辨识联合的错误处理
type ApiError =
| { type: "validation"; errors: z.ZodError }
| { type: "not_found"; resource: string }
| { type: "unauthorized"; message: string }
| { type: "internal"; error: Error };
class AppError extends Error {
constructor(public readonly error: ApiError) {
super(error.type);
}
}
function errorHandler(
err: Error,
req: Request,
res: Response,
next: NextFunction,
) {
if (err instanceof AppError) {
switch (err.error.type) {
case "validation":
return res.status(400).json({ errors: err.error.errors.errors });
case "not_found":
return res
.status(404)
.json({ message: `${err.error.resource} 未找到` });
case "unauthorized":
return res.status(401).json({ message: err.error.message });
case "internal":
return res.status(500).json({ message: "内部服务器错误" });
}
}
res.status(500).json({ message: "未知错误" });
}
app.use(errorHandler);
异步模式和错误处理
// 用于无异常错误处理的结果类型
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
async function fetchUser(id: string): Promise<Result<User>> {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
return { ok: false, error: new Error(`HTTP ${response.status}`) };
}
const user = await response.json();
return { ok: true, value: user };
} catch (error) {
return { ok: false, error: error as Error };
}
}
// 用法
const result = await fetchUser("123");
if (result.ok) {
console.log(result.value.name);
} else {
console.error(result.error.message);
}
// 类型安全Promise实用工具
async function race<T extends readonly unknown[]>(promises: {
[K in keyof T]: Promise<T[K]>;
}): Promise<T[number]> {
return Promise.race(promises);
}
async function all<T extends readonly unknown[]>(promises: {
[K in keyof T]: Promise<T[K]>;
}): Promise<T> {
return Promise.all(promises) as Promise<T>;
}
// 带类型推断的用法
const [user, posts, comments] = await all([
fetchUser("123"),
fetchPosts("123"),
fetchComments("123"),
]);
// 每个元素正确类型化
// 带指数退避的重试
async function retry<T>(
fn: () => Promise<T>,
options: {
maxAttempts: number;
initialDelay: number;
maxDelay: number;
backoffFactor: number;
},
): Promise<T> {
let lastError: Error;
let delay = options.initialDelay;
for (let attempt = 0; attempt < options.maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (attempt < options.maxAttempts - 1) {
await new Promise((resolve) => setTimeout(resolve, delay));
delay = Math.min(delay * options.backoffFactor, options.maxDelay);
}
}
}
throw lastError!;
}
// 用法
const user = await retry(() => fetchUser("123"), {
maxAttempts: 3,
initialDelay: 1000,
maxDelay: 10000,
backoffFactor: 2,
});
反模式
避免这些实践
// 错误:使用`any`绕过类型检查
function process(data: any): any {
return data.foo.bar.baz;
}
// 正确:使用unknown并缩小类型
function process(data: unknown): string {
if (isValidData(data)) {
return data.foo.bar.baz;
}
throw new Error("无效数据");
}
// 错误:无验证的类型断言
const user = JSON.parse(input) as User;
// 正确:运行时验证(使用zod、io-ts等)
import { z } from "zod";
const UserSchema = z.object({
id: z.string(),
email: z.string().email(),
name: z.string(),
});
const user = UserSchema.parse(JSON.parse(input));
// 错误:滥用非空断言运算符
function getUser(id: string): User {
return users.find((u) => u.id === id)!; // 如果未找到,崩溃
}
// 正确:处理undefined情况
function getUser(id: string): User | undefined {
return users.find((u) => u.id === id);
}
// 或显式抛出
function getUser(id: string): User {
const user = users.find((u) => u.id === id);
if (!user) {
throw new Error(`用户未找到: ${id}`);
}
return user;
}
// 错误:过度宽松的函数签名
function merge(a: object, b: object): object {
return { ...a, ...b };
}
// 正确:使用泛型保留类型
function merge<T extends object, U extends object>(a: T, b: U): T & U {
return { ...a, ...b };
}
// 错误:使用枚举(有运行时开销和怪癖)
enum Status {
Pending,
Active,
Completed,
}
// 正确:使用const对象或联合类型
const Status = {
Pending: "pending",
Active: "active",
Completed: "completed",
} as const;
type Status = (typeof Status)[keyof typeof Status];
// 错误:意外接口合并
interface Config {
port: number;
}
interface Config {
host: string;
}
// 现在Config同时有port和host - 通常无意
// 正确:使用类型别名当你不想要合并时
type Config = {
port: number;
host: string;
};
// 错误:忽略strictNullChecks问题
function getLength(str: string | null): number {
return str.length; // 如果null,运行时错误
}
// 正确:正确处理null
function getLength(str: string | null): number {
return str?.length ?? 0;
}