【问题标题】:How to type a color prop?如何键入颜色道具?
【发布时间】:2021-09-04 21:43:58
【问题描述】:

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

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

我的组件的类型定义:

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


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

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

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

谢谢

【问题讨论】:

    标签: javascript css reactjs typescript colors


    【解决方案1】:

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


    无论如何,如果您真的想从 typescript 中为您的 color 值进行类型检查,那么让我们从 w3c 颜色属性 description 开始:

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

    playground link 适用于那些不需要解释和直接查看代码的人。


    嗯,color keywordcurrentColortransparentinherit 非常简单:

    type Color = ColorValue | ColorKeyword | 'currentColor' | 'transparent' | 'inherit'
    
    type ColorKeyword =
        | "black"
        | "silver"
        | "gray"
        ...
        | "rebeccapurple"    
    

    棘手的部分是&lt;color value&gt;

    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 打字稿限制。

    让我们介绍一些辅助类型来处理数字。我们必须检查百分比不大于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 类型以修剪两端多余的空格:

    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'
    

    playground link

    【讨论】:

    • 哇,为这个答案鼓掌!
    猜你喜欢
    • 2018-11-22
    • 1970-01-01
    • 2021-12-23
    • 2021-05-18
    • 2019-09-28
    • 2021-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多