如何生成元素为Template Literal Types类型的数组?UNO牌组类型安全报错
问题原因
TS 默认会将你定义的[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "S", "R", "D"]推导为(string | number)[]类型,而非你期望的(NumberCard | SpecialCard)联合类型。当你使用模板字符串拼接color和v时,TS 无法识别拼接后的结果严格匹配CardType的范围,就会抛出类型不匹配的错误。
解决方案
有两种常用的解决方式,都可以消除报错同时保证类型安全:
方案1:显式指定数值数组类型 + 合法类型断言
你的业务逻辑本身已经保证了生成的卡牌一定符合CardType规则,直接显式指定数组类型,同时告诉TS你确认拼接结果类型合法即可,是最简便的处理方式:
type SpecialCard = "S" | "R" | "D"; // Skip | Reverse | Draw 2 type NumberCard = number; type Color = "R" | "Y" | "G" | "B"; type WildCard = "W" | "W4"; type CardType = `${Color}${(NumberCard | SpecialCard)}` | WildCard; // 先定义卡牌值的联合类型 type CardValue = NumberCard | SpecialCard; const StandardDeck: CardType[] = (["R", "Y", "G", "B"] as Color[]) .map<CardType[]>((color) => // 显式指定数组类型为CardValue[] ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "S", "R", "D"] as CardValue[]) .map<CardType>((v) => `${color}${v}` as CardType) ).flat() // 后续可自行添加4张W和4张W4补全标准UNO牌组
方案2:使用类型守卫封装生成逻辑
如果你不想使用类型断言,可以封装类型守卫函数校验生成的卡牌是否合法,适合对类型校验要求更严谨的场景:
type SpecialCard = "S" | "R" | "D"; // Skip | Reverse | Draw 2 type NumberCard = number; type Color = "R" | "Y" | "G" | "B"; type WildCard = "W" | "W4"; type CardType = `${Color}${(NumberCard | SpecialCard)}` | WildCard; // 类型守卫函数校验卡牌合法性 function isCardType(card: string): card is CardType { const colorCardRule = /^[RYGB][0-9SRD]$/ return colorCardRule.test(card) || card === "W" || card === "W4" } const StandardDeck: CardType[] = (["R", "Y", "G", "B"] as Color[]) .map((color) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "S", "R", "D"].map((v) => { const card = `${color}${v}` if (!isCardType(card)) throw new Error(`非法卡牌生成: ${card}`) return card }) ).flat()
内容的提问来源于stack exchange,提问作者philk
相关产品推荐
相关产品推荐

