TypeScript 错误处理:Result 类型与 Never 模式
try/catch 的问题
// 这个函数可能抛出什么错误?从签名中无法得知
async function getUser(id: string): Promise<User> {
// 可能抛出:NotFoundError, DatabaseError, ValidationError, NetworkError
return await userRepo.findById(id);
}
// 调用者必须猜测要捕获什么
try {
const user = await getUser(id);
} catch (err) {
// err 是什么类型?不阅读实现就无法知道
}

Result 类型模式
type Ok<T> = { readonly ok: true; readonly value: T };
type Err<E> = { readonly ok: false; readonly error: E };
type Result<T, E = Error> = Ok<T> | Err<E>;
// 构造函数
const ok = <T>(value: T): Ok<T> => ({ ok: true, value });
const err = <E>(error: E): Err<E> => ({ ok: false, error });
// 使用 - 错误在类型签名中显式体现!
async function findUser(id: string): Promise<Result<User, UserError>> {
try {
const user = await db.findById(id);
if (!user) return err({ type: 'NOT_FOUND', id } as const);
return ok(user);
} catch (e) {
return err({ type: 'DB_ERROR', cause: e } as const);
}
}
// 调用者显式处理错误
const result = await findUser('123');
if (!result.ok) {
switch (result.error.type) {
case 'NOT_FOUND': return res.status(404).json({ error: 'User not found' });
case 'DB_ERROR': return res.status(500).json({ error: 'Database error' });
}
}
const user = result.value; // TypeScript 知道这是 User 类型

类型化错误层次结构
abstract class AppError extends Error {
abstract readonly code: string;
abstract readonly statusCode: number;
constructor(message: string, readonly cause?: unknown) {
super(message);
this.name = this.constructor.name;
}
toJSON() {
return {
error: this.name,
code: this.code,
message: this.message,
};
}
}
class NotFoundError extends AppError {
readonly code = 'NOT_FOUND';
readonly statusCode = 404;
constructor(resource: string, id: string) {
super(`${resource} with id '${id}' not found`);
}
}
class ValidationError extends AppError {
readonly code = 'VALIDATION_ERROR';
readonly statusCode = 400;
constructor(readonly fields: Record<string, string>) {
super('Validation failed');
}
}
class UnauthorizedError extends AppError {
readonly code = 'UNAUTHORIZED';
readonly statusCode = 401;
constructor() { super('Authentication required'); }
}
// 错误处理中间件
function errorHandler(err: unknown, req: Request, res: Response) {
if (err instanceof AppError) {
return res.status(err.statusCode).json(err.toJSON());
}
console.error('Unexpected error:', err);
return res.status(500).json({ error: 'Internal server error' });
}

Never 模式(穷举检查)
type PaymentMethod = 'credit_card' | 'paypal' | 'crypto';
function processPayment(method: PaymentMethod): void {
switch (method) {
case 'credit_card':
processCreditCard();
break;
case 'paypal':
processPaypal();
break;
case 'crypto':
processCrypto();
break;
default:
// TypeScript 确保这里不可达
const _exhaustive: never = method;
throw new Error(`Unhandled payment method: ${_exhaustive}`);
}
}
// 现在如果你向 PaymentMethod 添加 'bank_transfer',
// TypeScript 会在 never 赋值处报错,
// 强制你处理新情况
使用 andThen 进行链式调用
class Result<T, E> {
private constructor(
private readonly _ok: boolean,
private readonly _value?: T,
private readonly _error?: E
) {}
static ok<T, E>(value: T): Result<T, E> { return new Result(true, value); }
static err<T, E>(error: E): Result<T, E> { return new Result(false, undefined, error); }
get ok(): boolean { return this._ok; }
get value(): T { if (!this._ok) throw new Error('Result is Err'); return this._value!; }
get error(): E { if (this._ok) throw new Error('Result is Ok'); return this._error!; }
map<U>(fn: (value: T) => U): Result<U, E> {
if (this._ok) return Result.ok(fn(this._value!));
return Result.err(this._error!);
}
andThen<U>(fn: (value: T) => Result<U, E>): Result<U, E> {
if (this._ok) return fn(this._value!);
return Result.err(this._error!);
}
}
// 链式操作
const result = await findUser(userId)
.andThen(user => validateUserAge(user))
.andThen(user => checkSubscription(user))
.map(user => UserDto.from(user));
将错误纳入类型系统可以在编译时捕获错误处理问题。