如何类型检查对象键是否符合条件递归模板类型?
How to type check if object keys conform a conditional recursive template type?
问题很容易理解,举个例子。我想为 https://npmjs.com/package/classnames 实现严格类型保护的包装器,以类型检查我们的应用程序使用的 Tailwind class 名称。
到目前为止最接近的解决方案是这个例子:
// 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 class 字符串,而不是突出显示“block h- 1" 无效?
要解决您遇到的具体问题,我会将 ClassNames
更改为:
type ClassNames<R> = { [K in keyof R]: K extends ValidTailwind<K> ? R[K] : never };
而不是 ClassNames<R>
返回 R
或 never
,它 maps 每个 属性 name K
in R
R[K]
(已经存在的 属性)或 never
。因此错误应该只出现在有问题的属性上:
classNamesWrapper({
"bad-class": false, // error!
//~~~~~~~~~ <-- Type 'boolean' is not assignable to type 'never'
"block h-1": true // okay
});
(请注意,我没有花任何时间查看示例代码的其余部分以查看它是否合理;我已专门关注所问的问题。)
问题很容易理解,举个例子。我想为 https://npmjs.com/package/classnames 实现严格类型保护的包装器,以类型检查我们的应用程序使用的 Tailwind class 名称。
到目前为止最接近的解决方案是这个例子:
// 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 class 字符串,而不是突出显示“block h- 1" 无效?
要解决您遇到的具体问题,我会将 ClassNames
更改为:
type ClassNames<R> = { [K in keyof R]: K extends ValidTailwind<K> ? R[K] : never };
而不是 ClassNames<R>
返回 R
或 never
,它 maps 每个 属性 name K
in R
R[K]
(已经存在的 属性)或 never
。因此错误应该只出现在有问题的属性上:
classNamesWrapper({
"bad-class": false, // error!
//~~~~~~~~~ <-- Type 'boolean' is not assignable to type 'never'
"block h-1": true // okay
});
(请注意,我没有花任何时间查看示例代码的其余部分以查看它是否合理;我已专门关注所问的问题。)