如何输入颜色道具? [英] How to type a color prop?

查看:34
本文介绍了如何输入颜色道具?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的组件接受一个 overlay 属性,它应该是一个有效的 CSS 颜色.

My component accepts an overlay prop that is supposed to be a valid CSS color.

当我在样式对象中的 color 属性上 CTRL + Click 时,类型定义来自 node_modules 中的 csstype 文件夹.CSS 颜色属性的类型定义定义为 Property.Color.我将该类型导入到我的组件中并将其用作我的 overlay 道具的类型,但是当我尝试使用该组件时它最终成为 any.

When I CTRL + Click on a color property in style object, the type definition comes from a csstype folder inside node_modules. The type definition for CSS color property is defined as Property.Color. I imported that type into my component and used it as my overlay prop's type but it ends up being any when I try to use that component.

我的组件的类型定义:

import { Property } from "../node_modules/csstype/index";


export interface BlurredComponentProps {
  overlay?: Property.Color;
}

这是我使用组件时的样子:

Here is how it looks when I use the component:

所以我的问题是,如何正确键入一个应该只采用有效 CSS 颜色的道具,如果给出非颜色值,则给出错误?

So my question is, how to properly type a prop that is supposed to take only valid CSS colors and give error if non-color value is given?

谢谢

推荐答案

这个在 TypeScript 的类型系统中很难编码.我相信成熟的解析器可以在速度和准确性方面做得更好.

This one is pretty hard to encode in TypeScript's type system. I believe a full fledged parser can do a better job in both speed and accuracy.

无论如何,如果您真的想从打字稿中对 color 值进行一些类型检查,那么让我们从 w3c 颜色属性开始描述:

Anyway, if you really want to get some typecheking for your color values from typescript then let's start with w3c color property description:

Values: <color value> | <color keyword> | currentColor | transparent | inherit

playground 链接,适用于不需要解释和直接查看代码的内容.

playground link for those who don't need explanations and what to look right into the code.

好吧,color keywordcurrentColortransparentinherit 非常简单:

Well, color keyword, currentColor, transparent and inherit are pretty straightforward:

type Color = ColorValue | ColorKeyword | 'currentColor' | 'transparent' | 'inherit'

type ColorKeyword =
    | "black"
    | "silver"
    | "gray"
    ...
    | "rebeccapurple"    

棘手的部分是<颜色值>:

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).

16 进制 RGB 值 还是可以的:

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.否则为扁平"联合类型:

We have to introduce type variable T. Otherwise 'flat' union type:

type RGBColor = `#${Hex3}` | `#${Hex3}${Hex3}`

将由 16^3 + 16^6 个成分组成,远远超出联合类型的 100000 打字稿限制.

is going to consist of 16^3 + 16^6 constituents that's far beyound 100000 typescript limit for union types.

让我们介绍一些处理数字的辅助类型.我们必须检查百分比不大于 100% 并以 % 字符结尾.

Let's introduce some helper types to work with numbers. We have to check the percents are not greater than 100% and end with % character.

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:

Also color values must be integers and not greater than 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] 范围内的整数或百分比:

so, any color value can be encoded as an integer number in [0..255] range or a percent:

type IsColorValue<T extends string> = IsPercent<T> | Color255<T>

添加实用程序 Trim 类型以修剪两端的多余空格:

Adding utility Trim type to trim extra spaces on both ends:

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,我们需要不透明度.这里我们只要求输入任何有效数字或百分比:

For rgba/hsla we'll need opacity. Here we just ask for any valid number or a percent:

type Opacity<T extends string> = IsDecNumber<T> | IsPercent<T>

现在我们可以检查 rgba 值:

Now we can check rgba values:

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 添加学位检查器:

Adding degree checker for 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

最后我们可以讨论最后一种情况:

and finally we can cover the last cases:

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

所以我们的最终类型看起来像这样:

So our final type will look like that:

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'

游乐场链接

这篇关于如何输入颜色道具?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆