【问题标题】:How to type check if object keys conform a conditional recursive template type?如何键入检查对象键是否符合条件递归模板类型?
【发布时间】:2021-04-20 14:12:23
【问题描述】:

这个问题通过一个例子很容易理解。我想为https://npmjs.com/package/classnames 实现一个严格类型保护的包装器,以检查我们的应用使用的 Tailwind 类名。

到目前为止,最接近的解决方案是这个例子:

// Credits to https://dev.to/virtualkirill/make-your-css-safer-by-type-checking-tailwind-css-classes-2l14
type Colors = "red" | "purple" | "blue" | "green";
type Luminance = 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
type BgColor = `bg-${Colors}-${Luminance}`;
type Layout = "block" | "w-1" | "h-1";
type TailwindClass = BgColor | Layout;

type ValidTailwindClassSeparatedBySpace<S> = S extends `${infer Class} ${infer Rest}`
  ? Class extends TailwindClass
    ? `${Class} ${ValidTailwindClassSeparatedBySpace<Rest>}`
    : never
  : S extends `${infer Class}`
  ? Class extends TailwindClass
    ? S
    : never
  : never;

type ValidTailwind<T> = T extends ValidTailwindClassSeparatedBySpace<T> ? T : never;
type ClassNames<R> = keyof R extends ValidTailwind<keyof R> ? R : never;

function classNamesWrapper<R>(obj: ClassNames<R>): string {
  // All arguments would be passed to npmjs.com/package/classnames
  // For the example, just return empty string.
  return '';
}

classNamesWrapper({ 
  "bg-red-100": true, 
  "block w-1": true 
});

classNamesWrapper({ 
  "bad-class": false,  // only this key should be invalid
  "block h-1": true
});

此处提供的示例代码:Playground link

这可行,但错误与对象的特定键无关,而是与所有对象键相关。 TypeScript 还将突出显示 "block h-1" 以产生相同的错误:Type 'boolean' is not assignable to type 'never'.

如何进行输入,以便 TS 能够检测到只有“bad-class”键是无效的 Tailwind 类字符串,但不会将“block h-1”突出显示为无效?

【问题讨论】:

    标签: typescript tailwind-css class-names


    【解决方案1】:

    要解决您看到的具体问题,我会将ClassNames 更改为:

    type ClassNames<R> = { [K in keyof R]: K extends ValidTailwind<K> ? R[K] : never };
    

    不是ClassNames&lt;R&gt; 返回Rnever,而是maps R 中的每个属性名称KR[K](已经存在的属性)或never。因此,错误应该只出现在有问题的属性上:

    classNamesWrapper({
      "bad-class": false,  // error!
      //~~~~~~~~~ <-- Type 'boolean' is not assignable to type 'never'
      "block h-1": true // okay
    });
    

    (请注意,我没有花任何时间查看示例代码的其余部分是否合理;我已特别关注所提出的问题。)

    Playground link to code

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-24
      • 2017-12-10
      • 2011-02-08
      • 1970-01-01
      • 2013-09-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多