TypeScript 模板文字类型

TypeScript template literal types

去年Sam Stephenson (ex Basecamp/HEY) posted a very interesting tweet about some developments on the stimulus js library he was working on which unfortunately never the saw the light of day due to the recent basecamp fallout events.

Playing with TypeScript template literal types . Here’s an example showing how we could map a Stimulus controller’s static targets = [...] array to an interface with all of the generated properties:

我最近一直在研究一些 Typescript,并试图破译那条特定的推文,但它似乎超出了我目前的理解水平。我已经在之前的 post 中尝试理解 NamedProperty type 但我仍然很困惑。

所以,谁能给我解释一下下面的代码?

type NamedProperty<Name extends string, T>
  = { [_ in 0 as Name]: T }

type UnionToIntersection<T>
  = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never
type ValueOf<T>
  = T[keyof T]

type TargetPropertyGroup<Name extends string>
  = NamedProperty<`has${Capitalize<Name>}Target`, boolean>
  & NamedProperty<`${Name}Target`, Element>
  & NamedProperty<`${Name}Targets`, Element[]>

// This type troubles me the most
type TargetProperties<Names extends string[]>
  = UnionToIntersection<ValueOf<{ [Name in Names[number]]: TargetPropertyGroup<Name> }>>

let controller!: TargetProperties<[ "form", "input", "result"]>

// Magical autocomplete happens here just after the dot!
let result = controller.

提前致谢!

让我们先从零碎开始,然后再尝试获得全部。 你已经得到 了。它让我们定义一个对象有一些 属性 和这个 属性 值的类型。

UnionToIntersection<T> 将并集变成交集。

type A = UnionToIntersection<{ a: number } | { b: string }>; 
// infers as { a: number } & { b: string };

您可以在 .

中找到有关其工作原理的精彩解释

ValueOf<T> 取一个类型和 returns 值类型的并集。

type B = ValueOf<{ a: number; b: string }>;
// infers as number | string;

TargetPropertyGroup<Name extends string> 接受一个字符串和 returns 一种包含三个属性的对象,这些属性的名称是根据传递的字符串构造的,值类型是预定义的。如您所见,它使用 template literals types.

现在TargetProperties<Names extends string[]>.

{ [Name in Names[number]]: TargetPropertyGroup<Name> } iterates 对作为类型参数传递的所有值应用 TargetPropertyGroup 以便对于传递给 TargetProperties 的每个字符串,它生成一个包含三个的类型属性。

type C<Names extends string[]> = { [Name in Names[number]]: TargetPropertyGroup<Name> };
let test = C<["form", "input", "result"]>;
// infers as 
// {
//    form: TargetPropertyGroup<"form">;
//    input: TargetPropertyGroup<"input">;
//    result: TargetPropertyGroup<"result">;
// }

最后两个步骤正在将 ValueOf<T> 应用于此,正如我们已经知道的那样,returns 类型的值作为联合:TargetPropertyGroup<"form"> | TargetPropertyGroup<"input"> | TargetPropertyGroup<"result"> 并应用 UnionToIntersection将类型转换为交集。结果类型看起来像一个对象,每个字符串包含三个字段传递给 TargetProperties.