如何用TypeScript模板字面量类型实现可组合的CardSize类型?
实现方案
版本一:无额外约束的最简实现
如果你不需要强制校验「同断点唯一值」「无重复值」的规则,只需要保证所有片段都是合法值、无string兜底,直接用递归模板字面量即可:
type Size = 'compact' | 'normal'; type Breakpoint = 'small' | 'medium' | 'large'; // 单个合法值 type SingleCardSize = Size | `${Size}@${Breakpoint}`; // 递归支持任意空格分隔的组合 type CardSize = SingleCardSize | `${SingleCardSize} ${CardSize}`;
校验效果
// 合法 const s1: CardSize = 'compact'; const s2: CardSize = 'compact@small normal@large'; const s3: CardSize = 'compact compact@small normal@medium'; // 非法(会抛出类型错误) const e1: CardSize = 'big'; // 非法值 const e2: CardSize = 'compact@xsmall'; // 非法断点
版本二:满足双约束的精准实现
如果需要严格满足「同一断点仅可出现一个值」「值顺序不影响校验」的规则,由于你的场景可选维度非常少(仅1个静态值位 + 3个断点值位),可以直接枚举所有合法排列组合,类型100%精准,无性能问题:
type Size = 'compact' | 'normal'; type Breakpoint = 'small' | 'medium' | 'large'; // 定义各独立位的可选片段,每个位最多出现1次 type StaticPart = Size; type SmallPart = `${Size}@small`; type MediumPart = `${Size}@medium`; type LargePart = `${Size}@large`; // 多片段全排列工具类型 type TwoPart<A extends string, B extends string> = `${A} ${B}` | `${B} ${A}`; type ThreePart<A extends string, B extends string, C extends string> = | `${A} ${B} ${C}` | `${A} ${C} ${B}` | `${B} ${A} ${C}` | `${B} ${C} ${A}` | `${C} ${A} ${B}` | `${C} ${B} ${A}`; type FourPart<A extends string, B extends string, C extends string, D extends string> = | `${A} ${B} ${C} ${D}` | `${A} ${B} ${D} ${C}` | `${A} ${C} ${B} ${D}` | `${A} ${C} ${D} ${B}` | `${A} ${D} ${B} ${C}` | `${A} ${D} ${C} ${B}` | `${B} ${A} ${C} ${D}` | `${B} ${A} ${D} ${C}` | `${B} ${C} ${A} ${D}` | `${B} ${C} ${D} ${A}` | `${B} ${D} ${A} ${C}` | `${B} ${D} ${C} ${A}` | `${C} ${A} ${B} ${D}` | `${C} ${A} ${D} ${B}` | `${C} ${B} ${A} ${D}` | `${C} ${B} ${D} ${A}` | `${C} ${D} ${A} ${B}` | `${C} ${D} ${B} ${A}` | `${D} ${A} ${B} ${C}` | `${D} ${A} ${C} ${B}` | `${D} ${B} ${A} ${C}` | `${D} ${B} ${C} ${A}` | `${D} ${C} ${A} ${B}` | `${D} ${C} ${B} ${A}`; // 聚合所有合法组合 type SingleCardSize = StaticPart | SmallPart | MediumPart | LargePart; type TwoCardSize = | TwoPart<StaticPart, SmallPart> | TwoPart<StaticPart, MediumPart> | TwoPart<StaticPart, LargePart> | TwoPart<SmallPart, MediumPart> | TwoPart<SmallPart, LargePart> | TwoPart<MediumPart, LargePart>; type ThreeCardSize = | ThreePart<StaticPart, SmallPart, MediumPart> | ThreePart<StaticPart, SmallPart, LargePart> | ThreePart<StaticPart, MediumPart, LargePart> | ThreePart<SmallPart, MediumPart, LargePart>; type FourCardSize = FourPart<StaticPart, SmallPart, MediumPart, LargePart>; // 最终类型 type CardSize = SingleCardSize | TwoCardSize | ThreeCardSize | FourCardSize;
校验效果
// 合法 const s1: CardSize = 'compact'; const s2: CardSize = 'compact@small normal@large'; const s3: CardSize = 'normal@large compact@small'; // 顺序不同不报错 const s4: CardSize = 'compact compact@small normal@medium'; // 非法(会抛出类型错误) const e1: CardSize = 'compact@small normal@small'; // 同断点多个值 const e2: CardSize = 'compact compact'; // 重复值 const e3: CardSize = 'big@small'; // 非法size
内容的提问来源于stack exchange,提问作者Weasel
相关产品推荐
相关产品推荐

