typescript - 从 Typescript 类型中提取信息

标签 typescript string primitive

我是 Typescript 的新手,所以可能没有正确使用术语......请耐心等待

背景

我一直在尝试扩展诸如 string 之类的基元,并取得了一定的成功。我什至可以在我的扩展字符串上存储一些自定义类型信息,如下所示:

// yes, we can extends strings!
type ExtendStr<P extends string = string, MaxLen extends number = 0> = P & { parent: P, options: { max: MaxLen } }

// create a LongStr with max length of 256
type LongStr = ExtendStr<string, 256>

我能够检索存储在类型信息中的额外信息,如下所示:

// get the max length of a LongStr
type LongStrMax = LongStr['options']['max']
// yields 256... Hoorah! 

我什至可以扩展 LongStr 并正确地进行类型扩大/缩小:

// make a ShortStr that extends LongStr but has max length 8
type ShortStr = ExtendStr<LongStr, 8>

// two example variables
let short: ShortStr = 'Hello' as ShortStr 
let long: LongStr = 'Omg this is a very long string!!' as LongStr 

// widening conversion is allowed as it should
long = short 

// narrowing conversion gives compiler error... sweet! 
short = long 

但是,这似乎“隐藏”了存储的信息...

现在的问题

我想获取 ShortStr 的存储 'max'。我在类型信息中看到它...

但是当我试图得到它时...

type ShortStrMax = ShortStr['options']['max']

..它产生never.... 有办法吗???

Playground

最佳答案

当你写作时

type ShortStr = ExtendStr<LongStr, 8>

long = short

您似乎想要 ShortStr成为 LongStr子类型 .也就是说,每个 8 个或更少字符的字符串也是 256 个或更少字符的字符串。这就说得通了。但是你对 ExtendStr<T, N> 的定义说会有一个options.max类型属性 N . ShortStr因此需要有一个 options.max值为 both 的属性 8 256 .此类型没有值,因此此属性类型等同于 the never type事情开始变得奇怪。


从概念上讲,想象 length 更有意义字符串的属性。值为 ExtendStr<string, 256>应该有一个 length值为小于或等于 256 的非负整数的属性.您可以将其表示为 union type喜欢0 | 1 | 2 | ... | 254 | 255 | 256 .和 ExtendString<string, 8>应该有一个 length类型属性 0 | 1 | 2 | ... | 6 | 7 | 8 .现在一个字符串绝对有可能有一个 length 两种 类型的属性,因为后者严格来说是前者的子类型。因此,如果我们能够以编程方式生成给定 N 的正确联合类型的数字,我们可以写成ExtendStr就此而言。

这是一种方法:

type LessThan<N extends number, A extends number[] = []> =
    N extends A['length'] ? A[number] : LessThan<N, [A['length'], ...A]>;

type LessThanOrEqual<N extends number> = N | LessThan<N>

LessThan<N>类型是 tail-recursive conditional type变成一个数字 literal type喜欢10通过构建 tuple 变成小于它的非负整数的并集这些值并在元组长度为 N 时停止.所以5会变成[0, 1, 2, 3, 4]变成0 | 1 | 2 | 3 | 4 .

LessThanOrEqual<N>只是N的并集与 LessThan<N> , 所以 LessThanOrEqual<5>0 | 1 | 2 | 3 | 4 | 5 .

现在ExtendStr :

type ExtendStr<P extends string = string, MaxLen extends number = 0> =
    P &  { length: LessThanOrEqual<MaxLen> } 

请注意,不是创建幻象 parentoptions.max属性,我只使用字符串类型本身和现有的 length属性(property)。如果你愿意,你可以保持你的方式,但我没有看到它在这个例子中有多大用处。这取决于你。

还有一件事……您希望能够从类型中提取最大长度。也就是说,给定 ExtendStr<string, N> , 你想检索 N .现在,如果您检查 length property 你得到一个大联盟,你只想要那个联盟的最大成员。好吧,你可以这样做:

type Max<N extends number> = Exclude<N, LessThan<N>>

之所以可行,是因为 LessThan<3 | 5>将是 0 | 1 | 2 | 3 | 4Exclude<3 | 5, 0 | 1 | 2 | 3 | 4>5 .


那么,让我们试试看:

type LongStr = ExtendStr<string, 256>

type LongStrMax = Max<LongStr['length']>
/* type LongStrMax = 256 */

type ShortStr = ExtendStr<LongStr, 8>

let short: ShortStr = 'Hello' as ShortStr
let long: LongStr = 'Omg this is a very long string!!' as LongStr

long = short // okay
short = long // error

type ShortStrMax = Max<ShortStr['length']>;
//type ShortStrMax = 8

看起来不错!


上面的方法对我来说效果很好,但是递归条件类型可能很棘手,有时会导致编译器性能问题。如果发生这种情况,您可能希望恢复到当前版本,然后以其他方式处理该问题。

Playground link to code

关于typescript - 从 Typescript 类型中提取信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73268722/

相关文章:

javascript - 如何从对象类型 State 的字段捕获 onChange 事件

typescript : enforce generics to have some keys compulsory

c# - 字符串替换方法的优化

.net - 字符串类型是存储在堆上还是堆栈上?

javascript - 如何检查对象中的值是否是原始值?

angular - 在 Angular 2 + Immutable.js 中迭代(使用 *ngFor)

c# - 为什么我们在尝试用另一个字符串替换它时将 @ 与 "\"一起使用

c - 哪些 Smalltalk 风格支持在方法中编写 C 代码?

java - int 的哈希码

angularjs - typescript 错误没有成员