TypeScript 模板文字类型 - 如何推断数字类型? [英] TypeScript Template Literal Type - how to infer numeric type?

查看:27
本文介绍了TypeScript 模板文字类型 - 如何推断数字类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

// from a library
type T = null | "auto" | "text0" | "text1" | "text2" | "text3" | "text4";

//in my code
type N = Extract<T, `text${number}`> extends `text${infer R}` ? R : never

( TS操场)

对于上面的一段代码 N 将等价于 0";|1"|2"|3"|4".如何将其转换为数字类型,即 0 |1 |2 |3 |4?已经试过把 &number 在某些地方,比如 infer R &号码,但这些都不起作用.

For the above piece of code N will be equivalent to "0" | "1" | "2" | "3" | "4". How can I convert that to a numeric type, i.e. 0 | 1 | 2 | 3 | 4? Have already tried putting & number in some places, like infer R & number, but none of that works.

推荐答案

目前似乎不可能,但有一个解决方法.

Seems to be it is currently impossible but there is a workaround.

您可以为0..42范围内的数字创建Dictionary:

You can create Dictionary for numbers in range 0..42:

// from a library
type Texts<T extends PropertyKey> = T extends number ? `text${T}` : never

type T = null | "auto" | Texts<Enumerate<43>>;

type PrependNextNum<A extends Array<unknown>> = A['length'] extends infer T ? ((t: T, ...a: A) => void) extends ((...x: infer X) => void) ? X : never : never;

type EnumerateInternal<A extends Array<unknown>, N extends number> = { 0: A, 1: EnumerateInternal<PrependNextNum<A>, N> }[N extends A['length'] ? 0 : 1];

type Enumerate<N extends number> = EnumerateInternal<[], N> extends (infer E)[] ? E : never;

type Dictionary = {
    [Prop in Enumerate<43> as `${Prop}`]: Prop
}

//  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 ... 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42
type N =
    Extract<T, `text${number}`> extends `text${infer R}`
    ? R extends keyof Dictionary
    ? Dictionary[R]
    : never
    : never

尾递归 PR 之后可能会生成更长的范围合并

It might be possible to generate much longer range after Tail recursion PR will be merged

游乐场

更新 - 就像我承诺的

试试

type MAXIMUM_ALLOWED_BOUNDARY = 999

type Mapped<
    N extends number,
    Result extends Array<unknown> = [],
    > =
    (Result['length'] extends N
        ? Result
        : Mapped<N, [...Result, Result['length']]>
    )

// 0 , 1, 2 ... 998
type NumberRange = Mapped<MAXIMUM_ALLOWED_BOUNDARY>[number]


type Texts<T extends PropertyKey> = T extends number ? `text${T}` : never


type T = null | "auto" | Texts<NumberRange>;

type Dictionary = {
    [Prop in NumberRange as `${Prop}`]: Prop
}

//  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 ... 998
type N =
    Extract<T, `text${number}`> extends `text${infer R}`
    ? R extends keyof Dictionary
    ? Dictionary[R]
    : never
    : never

游乐场

您可以使用 TS 版本 4.5(每晚)在 TS playground 中尝试上述解决方案代码简单多了.

You can try above solution in TS playground with TS version 4.5 (nightly) The code is much simpler.

这里有 Mapped 的 javascript 表示:

Here you have javascript representation of Mapped:

const Mapped = (N: number, Result: number[] = []): number[] => {
    if (Result.length === N) {
        return Result
    }
    return Mapped(N, [...Result, Result.length])
}

没什么复杂的.尾递归.

Nothing complicated. Tail recursion.

从字符串到数字的简单转换.请使用TS夜间版

type MAXIMUM_ALLOWED_BOUNDARY = 999

type Mapped<
    N extends number,
    Result extends Array<unknown> = [],
    > =
    (Result['length'] extends N
        ? Result
        : Mapped<N, [...Result, Result['length']]>
    )


type NumberRange = Mapped<MAXIMUM_ALLOWED_BOUNDARY>[number]


type ConvertToNumber<T extends string, Range extends number> =
    (Range extends any
        ? (`${Range}` extends T
            ? Range
            : never)
        : never)

type _ = ConvertToNumber<'5', NumberRange> // 5
type __ = ConvertToNumber<'125', NumberRange> // 125

游乐场

附言对不起,我不擅长命名.

P.S. sorry for naming, I'm not strong in it.

这篇关于TypeScript 模板文字类型 - 如何推断数字类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆