如何使用模板文字类型项生成数组
How to generate array with Template Literal Types items
我有一个 UNO 卡片组,我希望它是类型安全的。
deck 数组中的每张卡片都由一个包含颜色和值的 2 字符字符串表示。我正在为 CardType
使用 Template Literal Type
。
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;
现在我想创建一组这样的卡片:
const StandardDeck: CardType[] = (["R", "Y", "G", "B"] as Color[])
.map<CardType[]>((color) =>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "S", "R", "D"].map<CardType>(
(v) => `${color}${v}`
)
).flat()
但是这在这一行给我一个错误:
(v) => `${color}${v}`
Type '`R${number}` | `Y${number}` | `G${number}` | `B${number}` | `R${string}` | `Y${string}` | `G${string}` | `B${string}`' is not assignable to type 'CardType'.
Type '`R${string}`' is not assignable to type 'CardType'.
Type 'string' is not assignable to type 'CardType'.
Type '`R${string}`' is not assignable to type '"BD"'.
Type 'string' is not assignable to type '"BD"'.
type SpecialCard = "S" | "R" | "D"; // Skip | Reverse | Draw 2
type NumberCard = number;
type Color = "R" | "Y" | "G" | "B";
type WildCard = "W" | "W4";
type Indexes = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type CardType = `${Color}${(NumberCard | SpecialCard)}` | WildCard;
const applyColor = <Colors extends Color[]>(colors: [...Colors]) =>
<ColorKeys extends Indexes | SpecialCard, ColorMap extends ColorKeys[]>(colorMap: [...ColorMap]) =>
colors
.map((color) =>
colorMap.map((v): `${Colors[number]}${ColorKeys}` => `${color}${v}`)
).flat()
const makeDeck = applyColor(["R", "Y", "G", "B"])
// ("R0" | "R4" | "R1" | "R2" | "R3" | "R5" | "R6" | "R7" | "R8" | "R9" | "RR" | "RS"
// | "RD" | "Y0" | "Y4" | "Y1" | "Y2" | "Y3" | "Y5" | "Y6" | "Y7" | "Y8" | "Y9" | "YR" | "YS" | "YD" | "G0" | ... 24 more ... | "BD")[]
// 28+24 = 52 elements in the union
// exactly same number of elements is in runtime value
const StandardDeck = makeDeck([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "S", "R", "D"])
说明
我使用 curry
模式使其更具可读性,并使用 variadic tuple types 进行推理
您可能已经注意到,为了推断类型,我使用了一个函数,而不仅仅是一个值。
如果你想输入复杂的东西,首先尝试推断每个函数参数。推理越严格越好。
将鼠标悬停在 applyColor(["R", "Y", "G", "B"])
上。你会看到数组是用每个元素推断出来的,而不仅仅是 string[]
。与 makeDeck
.
相同
推断基元元组很容易。您只需要为元组 Colors
创建泛型并应用适当的约束 extends Color[]
.
在第二个函数中,除了推断整个元组外,我还推断了一个元组元素ColorKeys
。我这样做是因为我在 (v) =>
${color}${v}`` 中使用它来输入 return 类型。
TypeScript 足够聪明,可以推断 ${color}${v}
.
的确切类型
如果您对函数参数推断感兴趣,可以阅读我的 article。它还没有完成,但已经有一些有用的解释和示例。
P.S。似乎 WildCard
在运行时不存在。
我有一个 UNO 卡片组,我希望它是类型安全的。
deck 数组中的每张卡片都由一个包含颜色和值的 2 字符字符串表示。我正在为 CardType
使用 Template Literal Type
。
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;
现在我想创建一组这样的卡片:
const StandardDeck: CardType[] = (["R", "Y", "G", "B"] as Color[])
.map<CardType[]>((color) =>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "S", "R", "D"].map<CardType>(
(v) => `${color}${v}`
)
).flat()
但是这在这一行给我一个错误:
(v) => `${color}${v}`
Type '`R${number}` | `Y${number}` | `G${number}` | `B${number}` | `R${string}` | `Y${string}` | `G${string}` | `B${string}`' is not assignable to type 'CardType'.
Type '`R${string}`' is not assignable to type 'CardType'.
Type 'string' is not assignable to type 'CardType'.
Type '`R${string}`' is not assignable to type '"BD"'.
Type 'string' is not assignable to type '"BD"'.
type SpecialCard = "S" | "R" | "D"; // Skip | Reverse | Draw 2
type NumberCard = number;
type Color = "R" | "Y" | "G" | "B";
type WildCard = "W" | "W4";
type Indexes = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type CardType = `${Color}${(NumberCard | SpecialCard)}` | WildCard;
const applyColor = <Colors extends Color[]>(colors: [...Colors]) =>
<ColorKeys extends Indexes | SpecialCard, ColorMap extends ColorKeys[]>(colorMap: [...ColorMap]) =>
colors
.map((color) =>
colorMap.map((v): `${Colors[number]}${ColorKeys}` => `${color}${v}`)
).flat()
const makeDeck = applyColor(["R", "Y", "G", "B"])
// ("R0" | "R4" | "R1" | "R2" | "R3" | "R5" | "R6" | "R7" | "R8" | "R9" | "RR" | "RS"
// | "RD" | "Y0" | "Y4" | "Y1" | "Y2" | "Y3" | "Y5" | "Y6" | "Y7" | "Y8" | "Y9" | "YR" | "YS" | "YD" | "G0" | ... 24 more ... | "BD")[]
// 28+24 = 52 elements in the union
// exactly same number of elements is in runtime value
const StandardDeck = makeDeck([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "S", "R", "D"])
说明
我使用 curry
模式使其更具可读性,并使用 variadic tuple types 进行推理
您可能已经注意到,为了推断类型,我使用了一个函数,而不仅仅是一个值。
如果你想输入复杂的东西,首先尝试推断每个函数参数。推理越严格越好。
将鼠标悬停在 applyColor(["R", "Y", "G", "B"])
上。你会看到数组是用每个元素推断出来的,而不仅仅是 string[]
。与 makeDeck
.
推断基元元组很容易。您只需要为元组 Colors
创建泛型并应用适当的约束 extends Color[]
.
在第二个函数中,除了推断整个元组外,我还推断了一个元组元素ColorKeys
。我这样做是因为我在 (v) =>
${color}${v}`` 中使用它来输入 return 类型。
TypeScript 足够聪明,可以推断 ${color}${v}
.
如果您对函数参数推断感兴趣,可以阅读我的 article。它还没有完成,但已经有一些有用的解释和示例。
P.S。似乎 WildCard
在运行时不存在。