javascript - Kuinka määrittää värin prop-tyyppi?


Komponenttini hyväksyy overlay-ominaisuuden, jonka pitäisi olla kelvollinen CSS-väri.

Kun minä CTRL + klikkaan tyylin kohteen color-ominaisuutta, tyyppimäärittely tulee csstype-kansiosta node_modulesin sisällä. CSS-väriominaisuuden tyyppimäärittely on määritelty Property.Color-tyyppinä. Tuo tyyppi tuotiin komponenttiini ja käytin sitä overlay-propin tyyppinä, mutta se päätyy any-tyypiksi, kun yritän käyttää kyseistä komponenttia.

Komponenttini tyypin määritelmä:

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


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

Tältä se näyttää, kun käytän komponenttia:

Joten kysymykseni on, kuinka kirjoittaa prop oikein, joka on tarkoitettu ottamaan vain kelvollisia CSS-värejä ja antaa virheen, jos annetaan ei-värinen arvo?

Kiitos

Vastaus #1

Tämän koodaaminen TypeScriptin tyyppijärjestelmään on melko vaikeaa. Uskon, että täysimittainen jäsentäjä voi tehdä paremman työn sekä nopeudessa että tarkkuudessa.


Joka tapauksessa, jos todella haluat saada jonkinlaista tyyppien tarkistusta color-arvoillesi Typescriptistä, aloitetaan w3c:n väriominaisuuden kuvauksesta:

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

leikkikenttälinkki niille, jotka eivät tarvitse selityksiä ja jotka haluavat katsoa koodia suoraan.


No, väriavainsana, currentColor, läpinäkyvä ja periytyvä ovat melko suoraviivaisia:

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

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

Vaikea osa on <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).

heksadesimaali RGB-arvo on vielä ihan ok:

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

Meidän täytyy ottaa käyttöön tyyppimuuttuja T. Muuten 'tasainen' yhtenäistyyppi:

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

tulee koostumaan 16^3 + 16^6 osasta, mikä on kaukana 100000 TypeScriptin yhdistelmien rajasta.

Otetaan käyttöön joitakin aputyyppejä numeroiden kanssa työskentelyä varten. Meidän on tarkistettava, ettei prosenttiosuudet ole suurempia kuin 100% ja että ne päättyvät %-merkkiin.

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

Värien arvotkin on oltava kokonaislukuja eivätkä ne saa olla suurempia kuin 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

joten mikä tahansa värin arvo voidaan koodata kokonaislukuna [0..255] välillä tai prosenttina:

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

Lisätään hyötytyyppi Trim ylimääräisten välilyöntien karsimiseen molemmista päistä:

type WhiteSpace = ' '
type Trim<T> = T extends `${WhiteSpace}${infer U}` 
    ? Trim<U> 
    : T extends `${infer U}${WhiteSpace}` 
        ? Trim<U> 
        : T;

Se riittää 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:n tapauksessa tarvitsemme läpinäkyvyyden. Tässä pyydämme vain mitä tahansa kelvollista numeroa tai prosenttia:

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

Nyt voimme tarkistaa rgba-arvot:

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

Lisätään asteen tarkistin 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

ja lopuksi voimme käsitellä viimeiset tapaukset:

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

Joten lopullinen tyypimme näyttää tältä:

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'

leikkipuiston linkki

Aiheeseen liittyviä artikkeleita