【问题标题】:Convert string TYPE into array of chars TYPE将字符串 TYPE 转换为字符数组 TYPE
【发布时间】:2020-11-16 19:51:53
【问题描述】:

在 TypeScript 4.1 中,哪个开发版本已经可以通过 npm 获得,支持 Recursive Conditional TypesTemplate literal types 这创造了一些非常有趣的机会

假设我们有以下类型

// type is '0123456';
const actualString = '0123456';

任务

将字符串按字符拆分为新数组,但应保留数组元素的类型

// Unfortunately, type is string[]
const chars1 = actualString.split('');

// Throws error: string[] is not assignable ['0', '1', '2', '3', '4', '5', '6']
const chars2: ['0', '1', '2', '3', '4', '5', '6'] = actualString.split('');

我对这个的看法

type StringToChars<BASE extends string> = BASE extends `${infer _}`
  ? BASE extends `${infer FIRST_CHAR}${infer REST}` // BASE is inferable
    ? [FIRST_CHAR, ...StringToChars<REST>] // BASE has at least one character
    : [] // BASE is empty string
  : string[]; // BASE is simple string

// type is ['0', '1', '2', '3', '4', '5', '6']
type Chars = StringToChars<'0123456'>;

问题

此解决方案适用于少于 14 个字符的字符串。

// Throws: Type instantiation is excessively deep and possibly infinite. (ts2589)
type LargeCharsArray = StringToChars<'0123456789 01234'>

显然它遇到了打字稿类型递归限制,在检查第 14 个字符后,它给我们留下了[&lt;first 14 characters&gt;, ...any[]]

问题

这个递归类型调用看起来很糟糕,所以我想知道,有没有更可靠的方法将字符串类型转换为字符类型的数组?

playground link

【问题讨论】:

    标签: typescript types beta


    【解决方案1】:

    存在相当浅的递归限制,并且实现模板文字类型的拉取请求,microsoft/TypeScript#40336 提到,当您尝试一次拉开一个字符的字符串时,这是一个痛点。来自this comment的实现者:

    请注意,这些类型很快就会与递归深度限制器发生冲突。这是一个我仍在思考的正交问题。

    所以目前我认为没有一种解决方案适用于任意长度的字符串。


    也就是说,您可以通过一次拆分较大的块来玩弄限制,这样您就不必进行尽可能多的递归。可能有一些最佳方法可以做到这一点,但我只是通过反复试验来找到将最长可拆分字符串从 ~14 个字符增加到 ~80 个字符的方法:

    type StringToChars<T extends string> =
      string extends T ? string[] :
      T extends `${infer C0}${infer C1}${infer C2}${infer C3}${infer C4}${infer C5}${infer R}` ? [C0, C1, C2, C3, C4, C5, ...StringToChars<R>] :
      T extends `${infer C0}${infer C1}${infer C2}${infer C3}${infer R}` ? [C0, C1, C2, C3, ...StringToChars<R>] :
      T extends `${infer C0}${infer R}` ? [C0, ...StringToChars<R>] : []
    
    
    type WorksWith80 = StringToChars<'12345678911234567892123456789312345678941234567895123456789612345678971234567898'>;
    type Len80 = WorksWith80['length']; // 80
    
    type BreaksWith81 = StringToChars<'123456789112345678921234567893123456789412345678951234567896123456789712345678981'>;
    type Len81 = BreaksWith81['length']; // number
    

    您可以看到,我们不是只抓取一个字符,而是尝试抓取六个。如果失败了,我们会选择四个,然后退回到一个。


    你也许可以通过玩弄它来进一步推动极限,但我真的不明白这一点:这个算法是并且如果没有一堆递归的 cmets 似乎没有动力限制; TS 目前还没有为这些东西做好准备。希望@ahejlsberg 能在某个时候让这件事变得更好,我可以带着更好的消息回到这里。

    Playground link to code

    【讨论】:

      猜你喜欢
      • 2017-06-05
      • 1970-01-01
      • 1970-01-01
      • 2018-09-19
      • 2013-01-29
      • 1970-01-01
      • 1970-01-01
      • 2011-06-18
      相关资源
      最近更新 更多