TypeScript条件语句无法过滤类型值?求有效解决方案
TypeScript条件类型约束错误的解决方案
问题描述
我在使用TypeScript条件语句时遇到类型过滤失效的问题:当ErrorsType同时包含函数和字符串类型的值时,使用ErrorsType[T] extends Function ? Parameters<ErrorsType[T]> : []的条件判断,TypeScript无法正确收缩类型,导致构造函数的args参数出现约束错误:
Type 'Function & { test: (reason: string) => string; ValueTooLow: string; }[T]' does not satisfy the constraint '(...args: any) => any'
我的代码如下:
export const BotErrors = { test: (reason: string) => `This error bc of ${reason}`, ValueTooLow: 'You can not reduce value below 0', }; type ErrorsType = typeof BotErrors; export default class SomeError<T extends keyof ErrorsType> extends Error { constructor(code: T, ...args: ErrorsType[T] extends Function ? Parameters<ErrorsType[T]> : []) { let msg: string | Function = BotErrors[code]; if (typeof msg === 'function') msg = msg(...args) as string; super(msg); } }
添加// @ts-nocheck能暂时绕过错误,但我希望找到不忽略错误的正规解决办法。
解决方法
方法一:用Extract工具类型精准提取函数类型
问题核心是TypeScript在泛型上下文无法自动确认ErrorsType[T]是函数类型,导致Parameters工具类型收到不符合约束的输入。用Extract工具类型从ErrorsType[T]中提取出函数类型,就能让Parameters正常工作:
export default class SomeError<T extends keyof ErrorsType> extends Error { constructor(code: T, ...args: ErrorsType[T] extends Function ? Parameters<Extract<ErrorsType[T], Function>> : []) { let msg: string | Function = BotErrors[code]; if (typeof msg === 'function') { msg = msg(...(args as Parameters<typeof msg>)) as string; } super(msg); } }
方法二:给构造函数定义重载
如果觉得上面的写法繁琐,直接给构造函数定义重载,分别处理需要参数和不需要参数的错误码,TS会精准识别不同错误码对应的参数要求:
export default class SomeError<T extends keyof ErrorsType> extends Error { // 重载:处理需要参数的错误码 constructor(code: 'test', reason: string); // 重载:处理不需要参数的错误码 constructor(code: 'ValueTooLow'); // 构造函数实现 constructor(code: T, ...args: any[]) { let msg: string | Function = BotErrors[code]; if (typeof msg === 'function') { msg = msg(...args); } super(msg as string); } }
方法三:重构错误类型定义(扩展性更强)
如果后续需要新增更多错误码,可以提前定义清晰的联合类型,让TS能更好地推导类型:
// 定义每个错误的结构类型 type BotErrorDef = | { code: 'test', handler: (reason: string) => string } | { code: 'ValueTooLow', message: string }; // 用satisfies确保错误对象符合定义 export const BotErrors = { test: (reason: string) => `This error bc of ${reason}`, ValueTooLow: 'You can not reduce value below 0', } satisfies Record<BotErrorDef['code'], any>; type ErrorsType = typeof BotErrors; export default class SomeError<T extends BotErrorDef['code']> extends Error { constructor( code: T, ...args: Extract<BotErrorDef, { code: T }> extends { handler: infer F } ? Parameters<F> : [] ) { const def = BotErrors[code]; const msg = typeof def === 'function' ? def(...args) : def; super(msg); } }
这种方式新增错误码时,只需在BotErrorDef中添加对应的结构即可,维护性更强。
内容的提问来源于stack exchange,提问作者Bill.zhanxg
相关产品推荐
相关产品推荐

