2021-06-13:更新为 TS4.1+
不是真正在编译时,不是。有一个建议(现在位于 microsoft/TypeScript#41160) https://github.com/microsoft/TypeScript/issues/41160允许正则表达式验证字符串类型,但尚不清楚它是否会被实现。如果您想接受该建议并给它一个????并描述一个尚未列出的令人信服的用例,这不会有什么坏处(但它可能也不会真正有帮助)。
你可以try to use 模板文字类型 https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html#template-literal-types为此以编程方式生成一个大的union https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types它匹配每个可接受的字符串文字。如果您只需要三位左右的数字,这甚至可以工作:
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 https://www.typescriptlang.org/docs/handbook/2/generics.html 约束而不是做ValidColorString
具体类型。相反,有一个类似的类型AsValidColorString<T>
需要一个字符串类型T
and checks看看它是否有效。如果是的话,就不管它了。如果不是,则返回一个与错误颜色“接近”的有效颜色字符串。例如:
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 之前的解决方案并创建一个名义上的子类型string
with a 用户定义的类型保护 https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards缩小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);
}
Usage:
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 代码链接 https://www.typescriptlang.org/play?useDefineForClassFields=true#code/HYQwtgpgzgDiDGEAEAVCYYBsQBcIBkBLPAJxEwDERDMkBvAKCWaRwE8ZkBVAYRCggAJCAA8AIoQDmxJAF4kAcgAMCpAB9FARlUaFAJh2KAzIYUAWUwFZTANlMB2UwA5TATlMBBUwCFTPU2KmAKKmFApMLOycSMLiUjLyvPxCohLSOOpI+AD2AO4QJPDJADxJArFpxAB8EcxRyABq5IQAJigAFiQQEJU4PNmY2SQAyjgkhMCSckgABgDEACR0FfE4AL5LK+kby6mrazMA3LVIAPSnrByNzW2d3b39gyNjE1PyAERzSt-vmZ-fml+Gn+Sj0QKQIKM4JBZmhXyUljh3xs4JO5wh8PsSKUTmxrmxHmx3mxPGxYmxQWxFGxIGxACNUSwzhcQfBsS1wcyMd8IOCAHQCpCaJQ2PQ2JBgIbIAV8v5zABmiveDBO9SQTUwrWGhDi6UeQ1G40mchOTPmmz22wtuuIOy2tutvTtloduxt6yOXIKJCGAEI0RckAA-EOhsPhiORqPRmOx0MBpBBEQwLpQKCEbLAJAp7ItACuiCgSBASDzwAzWbVOHauCQhCLOGy2SQ8GyGEwolYza6KegEGAOAYaxVoEgsAQyDQ7dwBGIBXI-WAUDG1AH9FVVyQZRS7umylM2ky+lMJiPFiP1iPdiPjiPLiP7iPXiPviP-iPgSPISPYQ30XtGSJHw5QuhkGg5PkhQlNuAE1H+k7ZABxQoEgoh4MALRFsuRqSFU0woWh-aYTEoFIAA-KgSAAFxIEoxxMmqHhQBqrT6s8OHIahIjocR2GvHhsimiwBHcURRbmnQEzygUSBiJoOxSTJYh6ApwDSSQslGKp6myWY2lKZY+kaWINgHORQnMBJKCIaBxRyVUOzWUhykOUsTm2WIRiuXQ7nunZZjeb5vR2ZYgU2X5JkOTM1EWZRhEYeJiySWpSnyUsinGSp6UpcZWnRWRsVWeFwX2Y5xWrHZehhc5XlmVRsUKPCKgqkyrZLhk-BoNxAAKPowEW8icfFvEvJMVQABR4D1fVQDRdAtgMQw0SJPFFkxLEtGxhqvMheEUShNHrTcW2jZIu1IGsACUch4VNOC9dk-XHCcbXLqwaEPf10ydR9M3jYwTKtYtJA0Z8IB0myXLZAA1iAbAnFdLUsK9GR0iAbS-Y9A3FlAXX3X9AOA8wrZPKD8LdcMmiUl6JA+iQCbBmGDMoJuCgghTVPvKo9ZIMA2QdWmUigHSHZdpc0Rs01SicwofLjXoRh6Hol0IyrQ4qgwo7QHAiBIAAcm2EzkAAklA7Trgxm4bSdOHTHxxoAGT0EgAD6LsAG7HcD22TDRYx5sgazPUyEykPKE5IMbeOfUWhNEyTS3ql7Tw+5I9EsMOSPMPKZbwDgFZ1sxycGqd40iDR9uSJdNEiIXSeapt3unRbRMLe1SBdNMpxzAA2koAC0rggP38oeP3FAALp0Fp4194Pw+j+PU9aZdZGnGnXJsNkeZILkIBru0ogADQd1I7Q4AVrcdxAOB5iQWZdHyeDLmXl3p8wmdA+3d0xzRUeY19eQcdAYJxBhicGbIEaHBpnTE+rYMA0Bki0bI0BgAKAyNDPmuRWA1gyGDCGvweYlmtk3HCWc25vVAdMfBbJ351nlEgca9YSEp1LqAy611gFfzej-Gaeg-7RxmtMLhrdQE0VAbFIOUNYZsF5nkBGqFMACBbkTasPpsHAAgNgoItMhjjXeB0ZAuQhiYBaEgGsRYiIQA5G-BGKpM5AA