一种在Typescript模板文字中标记任意字符串的方法

i2byvkas  于 2022-11-26  发布在  TypeScript
关注(0)|答案(2)|浏览(104)

我尝试做的事情与自TypeScript 4.1发布以来许多其他开发人员可能一直在尝试的事情完全相同:使用预定义模式严格键入所有字符串。
虽然我设法为日期字符串找到了一个很好的折衷方案,但我现在面临着十六进制颜色代码的挑战。
显然,尝试HEX = 0的幼稚方法|一个|一个人。|“E”|“F”然后声明

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

会因为太多型别的并集而使型别过于复杂。
很公平。
所以我想我至少应该在十六进制代码前面加上#的要求,这意味着我可以满足于这样的要求:

type HEX_CODE = `#${arbitraryString}`;

但是我想不出一个方法来让它工作。有没有人知道如何使它工作,或者也许另一个(也许更好)妥协的类型?

pw9qyyiw

pw9qyyiw1#

这不就是:

type HEX_CODE = `#${string}`;

打字场

tpgth1q7

tpgth1q72#

更新日期:2022年8月28日

查看此答案和更新的文章
这里有一个解决方案:

更新

现在,如果字符串长度不等于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;

// before reading keep in mind that `: never` indicates that this conditional type has reached an invalid state
// when type checking linter/compiler will throw an error on any invalid type instantion
// e.g. let a: string = 1
type Mapper<
    Arr extends ReadonlyArray<Elem>,
    Result extends string = ''
    > = 
    Arr extends [] // is the array empty?
      // yes, the array is empty
      ? Result
      //no, the array is not empty, 
      : Arr extends [infer H] // does the array contain a single value of type H with properties as follows?
          // here is a property of H such that H is valid in this context
          ? H extends Elem   // does H exend Elem?
          // yes, Arr extends [Elem]
          ? `${Result}${H}` // the type parameters are valid and Mapper is this type
          : never           // no, Mapper has invalid type parameters
          // no, the array contains more than 1 value
          : Arr extends readonly [infer H, ...infer Tail] // does Arr extend the type of an array with a single value of type H followed by the values within an array of type Tail? e.g. ['hellp', 1, 2, 3] or ['hello', 'world']
            // here is a property of Tail such that Tail is valid in this context
            ? Tail extends ReadonlyArray<Elem> // does Tail extends a ReaonlyArray containing elements of type Elem  e.g. [Elem] ?
              // yes, Tail fits our assumption
              ? H extends Elem // does H extend Elem? (same as above)
                ? Mapper<Tail, `${Result}${H}`>  // yes! now recurively build the `Result` type!
                : never // no, H is invalid 

              : never // no, Tail is the wrong type since it contains values of a type other than Elem
              
            : never // no, Arr doesn't extend an array of this shape;

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

Playground2

相关问题