如何输入颜色道具?
How to type a color prop?
我的组件接受一个 overlay
属性,它应该是一个有效的 CSS 颜色。
当我在 color
属性 样式对象上 CTRL + Click
时,类型定义来自 node_modules 中的 csstype
文件夹。 CSS 颜色 属性 的类型定义定义为 Property.Color
。我将该类型导入到我的组件中并将其用作我的 overlay
道具的类型,但当我尝试使用该组件时它最终变成了 any
。
我的组件的类型定义:
import { Property } from "../node_modules/csstype/index";
export interface BlurredComponentProps {
overlay?: Property.Color;
}
这是我使用该组件时的样子:
所以我的问题是,如何正确输入应该只采用有效 CSS 颜色并在给出非颜色值时给出错误的道具?
谢谢
这个很难在 TypeScript 的类型系统中编码。我相信一个成熟的解析器可以在速度和准确性方面做得更好。
无论如何,如果你真的想从打字稿中为你的 color
值进行一些类型检查,那么让我们从 w3c 颜色开始 属性 description:
Values: <color value> | <color keyword> | currentColor | transparent | inherit
playground link 对于那些不需要解释和直接查看代码的人。
嗯,color keyword
、currentColor
、transparent
和 inherit
非常简单:
type Color = ColorValue | ColorKeyword | 'currentColor' | 'transparent' | 'inherit'
type ColorKeyword =
| "black"
| "silver"
| "gray"
...
| "rebeccapurple"
棘手的部分是 <color value>
:
The color can be specified as
* a hexadecimal RGB value: #faf or #ffaaff
* a RGB value: rgb(255, 160, 255) or rgb(100%, 62.5%, 100%)
Each value is from 0 to 255, or from 0% to 100%.
* a RGBA value: rgba(255, 160, 255, 1) or rgba(100%, 62.5%, 100%, 1)
This variant includes an “alpha” component to allow
specification of the opacity of a color. Values are
in the range 0.0 (fully transparent) to 1.0 (fully opaque).
* a HSL value: hsl(0, 100%, 50%)
A triple (hue, saturation, lightness). hue is an
angle in degrees. saturation and lightness are
percentages (0-100%).
* a HSLA value: hsla(0, 100%, 50%, 1)
This variant includes an “alpha” component to allow
specification of the opacity of a color. Values are
in the range 0.0 (fully transparent) to 1.0 (fully opaque).
hexadecimal RGB value
还可以:
type HexDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
type Hex3 = `${HexDigit}${HexDigit}${HexDigit}`
type RGBColor<T extends string> =
Lowercase<T> extends `#${Hex3}`
? T
: Lowercase<T> extends `#${Hex3}${infer Rest}`
? Rest extends Hex3
? T
: never
: never
我们必须引入类型变量T
。否则 'flat' 联合类型:
type RGBColor = `#${Hex3}` | `#${Hex3}${Hex3}`
将由 16^3 + 16^6
成分组成,这远远超出了联合类型的 100000
typescript 限制。
让我们介绍一些处理数字的辅助类型。我们必须检查百分比不大于 100%
并以 %
字符结尾。
type DecDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type Digits0to4 = '0' | '1' | '2' | '3' | '4'
type OnlyDecDigits<T extends string> =
T extends `${DecDigit}${infer Rest}`
? Rest extends ''
? 1
: OnlyDecDigits<Rest>
: never
type IsDecNumber<T extends string> =
T extends `${infer Integer}.${infer Fractional}`
? Integer extends ''
? OnlyDecDigits<Fractional>
: Fractional extends ''
? OnlyDecDigits<Integer>
: OnlyDecDigits<Integer> & OnlyDecDigits<Fractional>
: OnlyDecDigits<T>
type IntegerPart<T extends string> =
T extends `${infer I}.${infer F}`
? I
: T
type IsInteger<T extends string> =
1 extends IsDecNumber<T>
? T extends IntegerPart<T>
? 1
: never
: never
type Less100<T extends string> =
IsDecNumber<T> extends 1
? IntegerPart<T> extends `${DecDigit}` | `${DecDigit}${DecDigit}` | '100'
? 1
: never
: never
type IsPercent<T extends string> =
'0' extends T
? 1
: T extends `${infer P}%`
? Less100<P>
: never
另外颜色值必须是整数且不能大于255
:
type Color255<T extends string> =
1 extends IsInteger<T>
? T extends `${DecDigit}`
| `${DecDigit}${DecDigit}`
| `1${DecDigit}${DecDigit}`
| `2${Digits0to4}${DecDigit}`
| `25${Digits0to4 | '5'}`
? 1
: never
: never
因此,任何颜色值都可以编码为 [0..255]
范围内的整数或百分比:
type IsColorValue<T extends string> = IsPercent<T> | Color255<T>
添加实用程序 Trim
类型到 trim 两端的额外空格:
type WhiteSpace = ' '
type Trim<T> = T extends `${WhiteSpace}${infer U}`
? Trim<U>
: T extends `${infer U}${WhiteSpace}`
? Trim<U>
: T;
够了 rgb
:
type RGB<T extends string> =
T extends `rgb(${infer R},${infer G},${infer B})`
? '111' extends `${IsColorValue<Trim<R>>}${IsColorValue<Trim<G>>}${IsColorValue<Trim<B>>}`
? T
: never
: never
对于rgba
/hsla
,我们需要不透明度。在这里我们只要求任何有效数字或百分比:
type Opacity<T extends string> = IsDecNumber<T> | IsPercent<T>
现在我们可以检查 rgba
值:
type RGBA<T extends string> =
T extends `rgba(${infer R},${infer G},${infer B},${infer O})`
? '1111' extends `${IsColorValue<Trim<R>>}${IsColorValue<Trim<G>>}${IsColorValue<Trim<B>>}${Opacity<Trim<O>>}`
? T
: never
: never
为 hsl
/hsla
添加学位检查器:
type Degree<T extends string> =
1 extends IsInteger<T>
? T extends `${DecDigit}`
| `${DecDigit}${DecDigit}`
| `${'1' | '2'}${DecDigit}${DecDigit}`
| `3${Digits0to4 | '5'}${DecDigit}`
| '360'
? 1
: never
: never
最后我们可以涵盖最后的案例:
type HSL<T extends string> =
T extends `hsl(${infer H},${infer S},${infer L})`
? `111` extends `${Degree<Trim<H>>}${IsPercent<Trim<S>>}${IsPercent<Trim<L>>}`
? T
: never
:never
type HSLA<T extends string> =
T extends `hsla(${infer H},${infer S},${infer L},${infer O})`
? `1111` extends `${Degree<Trim<H>>}${IsPercent<Trim<S>>}${IsPercent<Trim<L>>}${Opacity<Trim<O>>}`
? T
: never
:never
因此我们的最终类型将如下所示:
type ColorValue<T extends string> = HexColor<T> | RGB<T> | RGBA<T> | HSL<T> | HSLA<T>
type Color<T extends string> = ColorValue<T> | ColorKeyword | 'currentColor' | 'transparent' | 'inherit'
我的组件接受一个 overlay
属性,它应该是一个有效的 CSS 颜色。
当我在 color
属性 样式对象上 CTRL + Click
时,类型定义来自 node_modules 中的 csstype
文件夹。 CSS 颜色 属性 的类型定义定义为 Property.Color
。我将该类型导入到我的组件中并将其用作我的 overlay
道具的类型,但当我尝试使用该组件时它最终变成了 any
。
我的组件的类型定义:
import { Property } from "../node_modules/csstype/index";
export interface BlurredComponentProps {
overlay?: Property.Color;
}
这是我使用该组件时的样子:
所以我的问题是,如何正确输入应该只采用有效 CSS 颜色并在给出非颜色值时给出错误的道具?
谢谢
这个很难在 TypeScript 的类型系统中编码。我相信一个成熟的解析器可以在速度和准确性方面做得更好。
无论如何,如果你真的想从打字稿中为你的 color
值进行一些类型检查,那么让我们从 w3c 颜色开始 属性 description:
Values: <color value> | <color keyword> | currentColor | transparent | inherit
playground link 对于那些不需要解释和直接查看代码的人。
嗯,color keyword
、currentColor
、transparent
和 inherit
非常简单:
type Color = ColorValue | ColorKeyword | 'currentColor' | 'transparent' | 'inherit'
type ColorKeyword =
| "black"
| "silver"
| "gray"
...
| "rebeccapurple"
棘手的部分是 <color value>
:
The color can be specified as
* a hexadecimal RGB value: #faf or #ffaaff
* a RGB value: rgb(255, 160, 255) or rgb(100%, 62.5%, 100%)
Each value is from 0 to 255, or from 0% to 100%.
* a RGBA value: rgba(255, 160, 255, 1) or rgba(100%, 62.5%, 100%, 1)
This variant includes an “alpha” component to allow
specification of the opacity of a color. Values are
in the range 0.0 (fully transparent) to 1.0 (fully opaque).
* a HSL value: hsl(0, 100%, 50%)
A triple (hue, saturation, lightness). hue is an
angle in degrees. saturation and lightness are
percentages (0-100%).
* a HSLA value: hsla(0, 100%, 50%, 1)
This variant includes an “alpha” component to allow
specification of the opacity of a color. Values are
in the range 0.0 (fully transparent) to 1.0 (fully opaque).
hexadecimal RGB value
还可以:
type HexDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
type Hex3 = `${HexDigit}${HexDigit}${HexDigit}`
type RGBColor<T extends string> =
Lowercase<T> extends `#${Hex3}`
? T
: Lowercase<T> extends `#${Hex3}${infer Rest}`
? Rest extends Hex3
? T
: never
: never
我们必须引入类型变量T
。否则 'flat' 联合类型:
type RGBColor = `#${Hex3}` | `#${Hex3}${Hex3}`
将由 16^3 + 16^6
成分组成,这远远超出了联合类型的 100000
typescript 限制。
让我们介绍一些处理数字的辅助类型。我们必须检查百分比不大于 100%
并以 %
字符结尾。
type DecDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type Digits0to4 = '0' | '1' | '2' | '3' | '4'
type OnlyDecDigits<T extends string> =
T extends `${DecDigit}${infer Rest}`
? Rest extends ''
? 1
: OnlyDecDigits<Rest>
: never
type IsDecNumber<T extends string> =
T extends `${infer Integer}.${infer Fractional}`
? Integer extends ''
? OnlyDecDigits<Fractional>
: Fractional extends ''
? OnlyDecDigits<Integer>
: OnlyDecDigits<Integer> & OnlyDecDigits<Fractional>
: OnlyDecDigits<T>
type IntegerPart<T extends string> =
T extends `${infer I}.${infer F}`
? I
: T
type IsInteger<T extends string> =
1 extends IsDecNumber<T>
? T extends IntegerPart<T>
? 1
: never
: never
type Less100<T extends string> =
IsDecNumber<T> extends 1
? IntegerPart<T> extends `${DecDigit}` | `${DecDigit}${DecDigit}` | '100'
? 1
: never
: never
type IsPercent<T extends string> =
'0' extends T
? 1
: T extends `${infer P}%`
? Less100<P>
: never
另外颜色值必须是整数且不能大于255
:
type Color255<T extends string> =
1 extends IsInteger<T>
? T extends `${DecDigit}`
| `${DecDigit}${DecDigit}`
| `1${DecDigit}${DecDigit}`
| `2${Digits0to4}${DecDigit}`
| `25${Digits0to4 | '5'}`
? 1
: never
: never
因此,任何颜色值都可以编码为 [0..255]
范围内的整数或百分比:
type IsColorValue<T extends string> = IsPercent<T> | Color255<T>
添加实用程序 Trim
类型到 trim 两端的额外空格:
type WhiteSpace = ' '
type Trim<T> = T extends `${WhiteSpace}${infer U}`
? Trim<U>
: T extends `${infer U}${WhiteSpace}`
? Trim<U>
: T;
够了 rgb
:
type RGB<T extends string> =
T extends `rgb(${infer R},${infer G},${infer B})`
? '111' extends `${IsColorValue<Trim<R>>}${IsColorValue<Trim<G>>}${IsColorValue<Trim<B>>}`
? T
: never
: never
对于rgba
/hsla
,我们需要不透明度。在这里我们只要求任何有效数字或百分比:
type Opacity<T extends string> = IsDecNumber<T> | IsPercent<T>
现在我们可以检查 rgba
值:
type RGBA<T extends string> =
T extends `rgba(${infer R},${infer G},${infer B},${infer O})`
? '1111' extends `${IsColorValue<Trim<R>>}${IsColorValue<Trim<G>>}${IsColorValue<Trim<B>>}${Opacity<Trim<O>>}`
? T
: never
: never
为 hsl
/hsla
添加学位检查器:
type Degree<T extends string> =
1 extends IsInteger<T>
? T extends `${DecDigit}`
| `${DecDigit}${DecDigit}`
| `${'1' | '2'}${DecDigit}${DecDigit}`
| `3${Digits0to4 | '5'}${DecDigit}`
| '360'
? 1
: never
: never
最后我们可以涵盖最后的案例:
type HSL<T extends string> =
T extends `hsl(${infer H},${infer S},${infer L})`
? `111` extends `${Degree<Trim<H>>}${IsPercent<Trim<S>>}${IsPercent<Trim<L>>}`
? T
: never
:never
type HSLA<T extends string> =
T extends `hsla(${infer H},${infer S},${infer L},${infer O})`
? `1111` extends `${Degree<Trim<H>>}${IsPercent<Trim<S>>}${IsPercent<Trim<L>>}${Opacity<Trim<O>>}`
? T
: never
:never
因此我们的最终类型将如下所示:
type ColorValue<T extends string> = HexColor<T> | RGB<T> | RGBA<T> | HSL<T> | HSLA<T>
type Color<T extends string> = ColorValue<T> | ColorKeyword | 'currentColor' | 'transparent' | 'inherit'