用TypeScript模板字面量实现国际象棋代数记法遇TS2590错误求解
解决TypeScript模板字面量生成过大连集类型导致的TS2590错误
我尝试用TypeScript模板字面量表示国际象棋代数记法,定义相关类型后,在AlgebraicMoveNotationBase处遇到以下错误:
TS2590: Expression produces a union type that is too complex to represent.
经计算,AlgebraicMoveNotation类型的组合数高达约35亿种,直接生成所有可能类型完全不可行。需求是让TypeScript仅验证赋值的字面量是否合法,而非预解析生成所有可能的类型。
简化复现代码
// 定义棋盘行列 type File = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h'; type Rank = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8'; type Square = `${File}${Rank}`; // 棋子类型 type Piece = 'K' | 'Q' | 'R' | 'B' | 'N' | ''; // 吃子标记 type Capture = 'x' | ''; // 将军/将死标记 type Check = '+' | '#' | ''; // 原始实现:生成所有可能的联合类型,导致错误 type AlgebraicMoveNotationBase = `${Piece}${File | Rank | ''}${Capture}${Square}${Check}`; type AlgebraicMoveNotation = AlgebraicMoveNotationBase | 'O-O' | 'O-O-O'; // 王翼/后翼易位 // 赋值时触发错误 const move: AlgebraicMoveNotation = 'e4';
解决方案:字面量合法性验证而非生成全量类型
通过递归条件类型验证输入的字面量是否符合规则,避免生成庞大的联合类型。核心思路是用类型检查逻辑替代枚举所有可能的组合:
// 基础类型定义不变 type File = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h'; type Rank = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8'; type Square = `${File}${Rank}`; type Piece = 'K' | 'Q' | 'R' | 'B' | 'N'; type Capture = 'x'; type Check = '+' | '#'; // 验证易位记法 type IsCastling<T extends string> = T extends 'O-O' | 'O-O-O' ? true : false; // 验证常规走法记法的结构 type IsRegularMove<T extends string> = T extends `${infer P extends Piece | ''}${infer D extends File | Rank | ''}${infer C extends Capture | ''}${infer S extends Square}${infer Ch extends Check | ''}` ? (D extends '' ? true : (P extends '' ? false : true)) // 无棋子标记时不能加行列区分符 : false; // 总验证类型 type IsAlgebraicMove<T extends string> = IsCastling<T> extends true ? true : IsRegularMove<T> extends true ? true : false; // 使用类型断言+验证类型来确保字面量合法 type AlgebraicMoveNotation<T extends string> = IsAlgebraicMove<T> extends true ? T : never; // 合法赋值示例(无错误) const validMove1: AlgebraicMoveNotation<'e4'> = 'e4'; const validMove2: AlgebraicMoveNotation<'Nf3'> = 'Nf3'; const validMove3: AlgebraicMoveNotation<'Qxd7#'> = 'Qxd7#'; const validMove4: AlgebraicMoveNotation<'O-O'> = 'O-O'; // 非法赋值示例(触发TypeScript错误) const invalidMove1: AlgebraicMoveNotation<'e9'> = 'e9'; // 行号非法 const invalidMove2: AlgebraicMoveNotation<'x e4'> = 'x e4'; // 格式错误 const invalidMove3: AlgebraicMoveNotation<'Ne'> = 'Ne'; // 缺少目标格子
关键说明
- 不再生成所有可能的联合类型,而是通过条件类型对输入的字面量进行结构和合法性检查
- 利用
infer关键字拆解模板字面量的各个部分,逐一验证规则 - 通过
AlgebraicMoveNotation<T>的泛型约束,仅在赋值时检查字面量是否符合格式,避免预计算所有组合
内容的提问来源于stack exchange,提问作者EvrCopy
相关产品推荐
相关产品推荐

