如何解决TS2590:表达式生成的联合类型过于复杂无法表示
我把应用中的API错误逻辑简化成如下代码,运行时会抛出错误:Expression produces a union type that is too complex to represent.
- 如何避免该错误?(缩小API规模不可行)
- 如何让TypeScript忽略该错误/提高联合类型上限?
3.(可选)该错误为何会发生?我了解到联合类型上限为100000,但示例代码仅包含5种错误类型,其中第一种有10个错误,不清楚具体原因。
- 使用
$Values替代ValuesType - 更换不同的TypeScript 4.x版本(无法降级到3.4)
- 使用类型别名
- 使用唯一错误码
- 用
$Values<typeof ErrType1 & typeof ErrType2>替代$Values<typeof ErrType1> | $Values<typeof ErrType2>
import type { $Values } from 'utility-types'; const MESSAGES = { msg1: 'msg1', msg2: 'msg2', msg3: 'msg3', msg4: 'msg4', msg5: 'msg5', } as const; const ErrType1 = { Err_1_1: { code: 1, }, Err_1_2: { code: 2, }, Err_1_3: { code: 3, }, Err_1_4: { code: 4, }, Err_1_5: { code: 5, }, Err_1_6: { code: 6, }, Err_1_7: { code: 7, }, Err_1_8: { code: 8, }, Err_1_9: { code: 9, }, Err_1_10: { code: 10, }, } as const; const ErrType2 = { Err_2_1: { code: 1, }, } as const; const ErrType3 = { Err_3_1: { code: 1, }, } as const; const ErrType4 = { Err_4_1: { code: 1, }, } as const; const ErrType5 = { Err_5_1: { code: 1, }, } as const; type ApiErrors = { [MESSAGES.msg1]: $Values<typeof ErrType1>; [MESSAGES.msg2]: $Values<typeof ErrType1> | $Values<typeof ErrType2>; [MESSAGES.msg3]: $Values<typeof ErrType1> | $Values<typeof ErrType3>; [MESSAGES.msg4]: $Values<typeof ErrType1> | $Values<typeof ErrType4>; [MESSAGES.msg5]: $Values<typeof ErrType1> | $Values<typeof ErrType5>; }; type Response<Message extends $Values<typeof MESSAGES>> = { error: ApiErrors[Message]; }; // eslint-disable-next-line @typescript-eslint/no-unused-vars function test<Message extends $Values<typeof MESSAGES>>(): Response<Message> { return { error: ErrType1.Err_1_1, }; }
1. 避免错误的可行方法
核心是减少TypeScript需要计算的联合类型复杂度,以下是几种有效方案:
方案一:抽离公共联合类型,避免重复展开
把重复出现的$Values<typeof ErrType1>抽成公共类型别名,复用后减少TypeScript重复计算同一联合类型的次数:
type BaseErr = $Values<typeof ErrType1>; type ApiErrors = { [MESSAGES.msg1]: BaseErr; [MESSAGES.msg2]: BaseErr | $Values<typeof ErrType2>; [MESSAGES.msg3]: BaseErr | $Values<typeof ErrType3>; [MESSAGES.msg4]: BaseErr | $Values<typeof ErrType4>; [MESSAGES.msg5]: BaseErr | $Values<typeof ErrType5>; };
方案二:用条件类型替代索引访问
修改Response类型,通过条件类型直接映射消息与错误类型,降低泛型推导时的类型展开复杂度:
type Response<Message extends $Values<typeof MESSAGES>> = { error: Message extends typeof MESSAGES.msg1 ? $Values<typeof ErrType1> : Message extends typeof MESSAGES.msg2 ? $Values<typeof ErrType1> | $Values<typeof ErrType2> : Message extends typeof MESSAGES.msg3 ? $Values<typeof ErrType1> | $Values<typeof ErrType3> : Message extends typeof MESSAGES.msg4 ? $Values<typeof ErrType1> | $Values<typeof ErrType4> : $Values<typeof ErrType1> | $Values<typeof ErrType5>; };
方案三:放宽类型约束(牺牲部分精确性)
如果不需要极致的类型精确性,可以将错误类型合并为一个通用联合类型:
type ApiErrorUnion = $Values<typeof ErrType1> | $Values<typeof ErrType2> | $Values<typeof ErrType3> | $Values<typeof ErrType4> | $Values<typeof ErrType5>; type Response<Message extends $Values<typeof MESSAGES>> = { error: ApiErrorUnion; };
2. 忽略错误或调整TypeScript限制
忽略单个错误
在报错代码行上方添加注释,精准忽略该错误:
// @ts-expect-error function test<Message extends $Values<typeof MESSAGES>>(): Response<Message> { return { error: ErrType1.Err_1_1, }; }
不推荐使用// @ts-ignore,它会忽略该行所有错误。
降低泛型检查严格度
TypeScript没有公开配置项直接提高联合类型上限,但可以通过编译选项--noStrictGenericChecks降低泛型检查的严格程度,间接减少这类错误的触发概率。注意:该选项会放宽其他泛型约束,需谨慎使用。
3. 错误发生的原因
你了解到的100000是TypeScript对显式声明的联合类型的节点上限,但你的问题出在泛型推导时的隐式类型展开:
当TypeScript处理Response<Message>时,需要为Message的每个可能值(5个)展开对应的ApiErrors[Message]类型,而每个ApiErrors[Message]又包含10个成员的BaseErr联合类型。TypeScript在推导泛型返回值时,会尝试将所有可能的组合展开成一个巨大的隐式联合类型,这个过程中生成的中间类型节点数远超预期,触发了内部的复杂度阈值,导致报错。
内容的提问来源于stack exchange,提问作者Vorty7

