【问题标题】:A way to mark arbitrary strings in Typescript Template Literals一种在 Typescript 模板文字中标记任意字符串的方法
【发布时间】:2021-03-30 15:24:34
【问题描述】:

我正在尝试做与自 TypeScript 4.1 发布以来许多其他开发人员可能一直在尝试的完全相同的事情:严格键入具有预定模式的所有字符串。

虽然我设法为日期字符串找到了一个不错的折衷方案,但我现在面临着十六进制颜色代码的挑战。

显然尝试 HEX = 0 | 的天真方式1 | ... | "E" | "F" 然后声明

type HEX_CODE = `#${HEX}${HEX}${HEX}${HEX}${HEX}${HEX}`;

通过联合太多类型使类型过于复杂。

很公平。

所以我想我会尝试至少在十六进制代码前添加 # 的要求,这意味着我会满足于:

type HEX_CODE = `#${arbitraryString}`;

但是我想不出办法让它工作。有谁知道如何使这项工作发挥作用,或者对类型进行另一种(也许更好)妥协?

【问题讨论】:

    标签: typescript template-literals


    【解决方案1】:

    这不就是这样吗:

    type HEX_CODE = `#${string}`;
    

    typescript playground

    【讨论】:

    • 详细地说,OP 对type HEX_CODE = `#${arbitraryString}`; 的想法是正确的。但是由于这里声明了一个类型,${} 内部的位需要是一个类型,而不是一个变量引用。
    • 根据实现它们的 PR microsoft/TypeScript#40598,这些被称为“模式文字类型”。
    【解决方案2】:

    这里有一个解决方案:

    更新

    现在,如果字符串长度不等于 6 则会引发错误

    type HexNumber = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
    type HexString = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
    type StringNumber<T extends number> = `${T}`
    type HEX = HexNumber | StringNumber<HexNumber> | HexString;
    
    type Check<T extends string, Cache extends readonly string[] = []> =
        T extends `${infer A}${infer Rest}`
        ? A extends HEX
        ? Check<Rest, [...Cache, A]> : A extends ''
        ? 1 : 2 : T extends '' ? Cache extends { length: 6 }
        ? Cache : 'String should have 6 chars. No more, no less' : never;
    
    type Elem = string;
    
    type Mapper<
        Arr extends ReadonlyArray<Elem>,
        Result extends string = ''
        > = Arr extends []
        ? Result
        : Arr extends [infer H]
        ? H extends Elem
        ? `${Result}${H}`
        : never
        : Arr extends readonly [infer H, ...infer Tail]
        ? Tail extends ReadonlyArray<Elem>
        ? H extends Elem
        ? Mapper<Tail, `${Result}${H}`>
        : never
        : never
        : never;
    
    
    type Result = Mapper<Check<'abcdef'>> // allow
    type Result2 = Mapper<Check<'00cdef'>> // allow
    type Result3 = Mapper<Check<'z0cdef'>> // not allow
    type Result4 = Mapper<Check<'00cdem'>> // not allow
    type Result5 = Mapper<Check<'aaaaa'>> // to few arguments
    
    

    Playground

    在实践中你只能将我的解决方案用于函数参数

    const hex = <T extends string, U extends {
        'valid': 'valid',
        'invalid': 'invalid',
    }[Check<T> extends string[] ? Mapper<Check<T>> extends T ? 'valid' : never : 'invalid']>(value: T, ...rest: U extends 'valid' ? [] : [never]) => value
    
    const result = hex('aaaaaf') // ok
    const result2 = hex('aaaaaZ') // error
    

    Playground 2

    【讨论】:

    • 虽然这看起来很有趣,但我如何在实际环境中使用它?比如说我想声明一个有限制的变量?有了这个,我似乎会创建 Result 类型的常量,它将它绑定到严格等于'abcdef',这已经足够是一个有效的 6 长度 HEX,但我希望我的变量保存任何可能的 6 长度 HEX 值(最好以#)为前缀
    • 你是绝对正确的。我做了一个更新。你只能将我的解决方案用于函数参数
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-12
    • 2017-07-19
    • 2010-09-09
    • 1970-01-01
    • 2010-12-24
    • 2019-04-25
    相关资源
    最近更新 更多