如何在TypeScript中定义css颜色的类型?

l5tcr1uw  于 2022-11-26  发布在  TypeScript
关注(0)|答案(6)|浏览(395)

下面是一个示例代码片段:

type Color = string;

interface Props {
    color: Color;
    text: string;
}

function Badge(props: Props) {
    return `<div style="color:${props.color}">${props.text}</div>`;
}

var badge = Badge({
    color: '#F00',
    text: 'Danger'
});

console.log(badge);

Playground
如果颜色无效,我会尝试得到一个构建错误,如下所示:

var badge = Badge({
    color: 'rgba(100, 100, 100)',
    text: 'Danger'
});

有没有办法定义Color,使它只允许匹配以下模式之一的字符串?

  • #FFF
  • #FFFFFF
  • rgb(5, 5, 5)
  • rgba(5, 5, 5, 1)
  • hsa(5, 5, 5)

我意识到有像redwhite这样的颜色,但如果Color可以接受这些颜色,这可能会使这个问题更难回答。

2ledvvac

2ledvvac1#

type RGB = `rgb(${number}, ${number}, ${number})`;
type RGBA = `rgba(${number}, ${number}, ${number}, ${number})`;
type HEX = `#${string}`;

type Color = RGB | RGBA | HEX;

'${...}'表示法在新的(^4.1)ts版本中可用。
https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html

zengzsys

zengzsys2#

有一个关于a type of string which matches a pattern(regex或其他东西)的提议,但是这个提议还没有实现。
因此,不幸的是,从TypeScript 2.2开始,您所要求的是不可能的。

dxpyg8gm

dxpyg8gm3#

在一般意义上还不能这样做,但是如果有一组定义良好的颜色,就可以使用常量和字符串类型:

type Color = "#FFFFFF" | "#FF0000" | "#0000FF";
const WHITE: Color = "#FFFFFF";
const RED: Color = "#FF0000";
const BLUE: Color = "#0000FF";

显然,如果您想允许 * 任何 * 颜色,这将是不实际的,但在现实中,您可能 * 确实 * 希望有可重用的颜色变量。
在我的项目中,我使用脚本从我的colors.css文件生成一个类似的文件,该文件定义了一系列CSS属性:

:root {
  --primary-red: #ff0000;
  --secondary-red: #993333;
  /* etc */
}

转换为:

export const primaryRed: Color = "#ff0000";
export const secondaryRed: Color = "#993333";
// etc
export type Color = "#ff0000" | "#993333" // | etc...

我会这样说:

import {primaryRed} from "./Colors.ts";

interface BadgeProps {
    color: Color;
    text: string;
}  

var badge = Badge({
    color: primaryRed,
    text: 'Danger'
});
uqdfh47h

uqdfh47h4#

从理论上讲,联合类型是可能的,但是如果你只是使用十六进制代码,你就有超过16,000,000种可能的组合,而typescript(至少4.6.3)不喜欢这种想法。
它已经被微软搁置了一年多了

crcmnpdw

crcmnpdw5#

对颜色有效性if (process.env.NODE_ENV === 'development')进行运行时检查是目前最合适的解决方案,因为TypeScript(从4.8.4开始)无法处理这样的复杂类型。
我试着创建一个HEX颜色类型,如果你只是以人类的身份阅读代码,它似乎应该可以工作,但实际上它失败了,因为任何比短格式CSS HEX颜色(如#fff)更重载的东西都失败了:

type HexLetter = 'a' | 'b' | 'c' | 'd' | 'e' | 'f';
type HexDigit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type HexPart = HexLetter | HexDigit;
type HexEncodedNumber = `${HexPart}${HexPart}`;

const hexEncodedNumberTestForSuccess: HexEncodedNumber = '0f';
const hexEncodedNumberTestForError: HexEncodedNumber = 'r3'; //* Type '"r3"' is not assignable to type 'HexEncodedNumber'.
// A single HexEncodedNumber works!
// This means we can have type safety defining a number from 0 to 255 in HEX format.

/** Length: 3 */
type HexCodeShort = `${HexPart}${HexPart}${HexPart}`;
/** Length: 6 */
type HexCodeClassic = `${HexEncodedNumber}${HexEncodedNumber}${HexEncodedNumber}`; //! Expression produces a union type that is too complex to represent. ts(2590)
// HexCodeClassic evaluates to 'any' type.
/** Length: 8 */
type HexCodeWithAlpha = `${HexCodeClassic}${HexEncodedNumber}`; // So this is `${any}aa` | `${any}ab` etc.
/** Length: 3 (short) | 6 (classic) | 8 (alpha) */
type HexCodeModern = HexCodeShort | HexCodeClassic | HexCodeWithAlpha; // Just 'any'.

type HexCodeCss = `#${HexCodeModern}`;
const hexCodeCssTestForEmergedAny: HexCodeCss = '#ICanSetAnythingHereWithNoTrouble'; // `#${any}`.
// Full CSS HEX code does not work.

因此,您只能确保CSS颜色的3个字符的完全类型安全,甚至不考虑大写字母。
作为一种解决方法,您可以构造一个函数,该函数接受r, g, b, a所有类型的HexEncodedNumber,并且TypeScript将对参数进行类型检查,但是如果您试图在返回语句中添加as const或'as'#${string}',则该函数的ReturnType<>将只是stringany
RGB的情况应该是差不多的,因为它是相同的可能的组合数量。

wfypjpf4

wfypjpf46#

看起来您在示例中使用的是React。一个简单的选项可能是导入CSSProperties并访问color属性。

import { CSSProperties } from "react";

const Entry = ({color}: {color: CSSProperties["color"]}) => {
  return (
    <div style={{color: color}}>
      This text will be colored.
    </div>
  );
};

这样做的好处是它可以很容易地提供任何形式的颜色:命名颜色,如yellowalicebluepowderblue,十六进制值,RGB,HSL或任何有效CSS字符串
这在功能上与使用string相同,但类型用法更有表现力。如果需要使用类型保护,请使用第一个答案。

相关问题