2021-06-13:针对 TS4.1+ 更新
不是真的在编译时,不。有一个suggestion (now at microsoft/TypeScript#41160) 允许正则表达式验证的字符串类型,但尚不清楚它是否会被实现。如果您想采纳该建议并给它一个 ? 并描述一个尚未列出的引人注目的用例,它不会受到伤害(但它可能也不会真正有帮助)。
您可以尝试为此使用template literal types 以编程方式生成一个大的union,它匹配每个可接受的字符串文字。如果您只需要三位数,这甚至可以工作:
type UCaseHexDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |
'8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
type HexDigit = UCaseHexDigit | Lowercase<UCaseHexDigit>
type ValidThreeDigitColorString = `#${HexDigit}${HexDigit}${HexDigit}`;
// type ValidThreeDigitColorString = "#000" | "#001" | "#002" | "#003" | "#004" | "#005"
// | "#006" | "#007" | "#008" | "#009" | "#00A" | "#00B" | "#00C" | "#00D" | "#00E"
// | "#00F" | "#00a" | "#00b" | "#00c" | "#00d" | // "#00e"
// | ... 10626 more ... | "#fff"
但由于此类模板文字类型只能处理数万成员的联合,因此如果您尝试使用六位数执行此操作,则会中断:
type ValidSixDigitColorString =
`#${HexDigit}${HexDigit}${HexDigit}${HexDigit}${HexDigit}${HexDigit}`; // error!
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Expression produces a union type that is too complex to represent
所以你必须使用一种解决方法。
一种解决方法是使用模板文字类型作为generic 约束,而不是使ValidColorString 成为具体类型。相反,有一个像AsValidColorString<T> 这样的类型,它接受一个字符串类型T 并检查它以查看它是否有效。如果是,它就会被单独留下。如果不是,则返回一个与错误颜色“接近”的有效颜色字符串。例如:
type ToHexDigit<T extends string> = T extends HexDigit ? T : 0;
type AsValidColorString<T extends string> =
T extends `#${infer D1}${infer D2}${infer D3}${infer D4}${infer D5}${infer D6}` ?
`#${ToHexDigit<D1>}${ToHexDigit<D2>}${ToHexDigit<D3>}${ToHexDigit<D4>}${ToHexDigit<D5>}${ToHexDigit<D6>}` :
T extends `#${infer D1}${infer D2}${infer D3}` ?
`#${ToHexDigit<D1>}${ToHexDigit<D2>}${ToHexDigit<D3>}` :
'#000'
const asTextProps = <T extends string>(
textProps: { color: T extends AsValidColorString<T> ? T : AsValidColorString<T> }
) => textProps;
这很复杂;大多数情况下,它会拆分字符串T 并检查每个字符,将坏字符转换为0。然后,不要将某些内容注释为 TextProps,而是调用 asTextProps 进行验证:
const textProps = asTextProps({
color: "#abc" // okay
})
const badTextProps = asTextProps({
color: "#00PS1E" // error
// ~~~~~
// Type '"#00PS1E"' is not assignable to type '"#00001E"'.(2322)
})
这在编译时有效,但可能比它的价值更麻烦。
最后,您可以退回到 TS4.1 之前的解决方案,并使用 user-defined type guard 将 string 的值缩小到它的范围内,并创建一个名义上的 string 子类型...然后跳过各种使用它的箍:
type ValidColorString = string & { __validColorString: true };
function isValidColorString(x: string): x is ValidColorString {
const re = /#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?/g; // you want hex, right?
return re.test(x);
}
用法:
const textProps: ITextProps = {
color: "#abc"
}; // error, compiler doesn't know that "#abc" is a ValidColorString
const color = "#abc";
if (isValidColorString(color)) {
const textProps2: ITextProps = {
color: color
}; // okay now
} else {
throw new Error("The world has ended");
}
后者并不完美,但至少可以让您更接近强制执行此类约束。
希望能给你一些想法;祝你好运!
Playground link to code