检查递归类型是否以特定类型结尾
Check if a recursive type ends with a certain type
我在 Typescript 中有以下类型定义:
type MU = ['I' | 'U' | 'M', MU] | '.'
我正在尝试编写助手:
type EndsWith<T extends MU, M extends MU> = T extends M ? any : T extends [infer _, infer Q] ? EndsWith<Q, M> : never
这将检查特定类型 T
是否以给定的 M
、 结尾,即 :
const a: EndsWith<['U', ['I', '.']], ['I', '.']> // types as any
const b: EndsWith<['U', '.'], ['I', '.']> // types as never
但是,EndsWith
不是我指定的别名类型。寻找解决方法:)
目前不支持循环条件类型。请参阅 microsoft/TypeScript#26980 以了解建议解除此约束的未决问题。目前,它们是被禁止的。
请注意,很可能 欺骗 编译器评估循环条件类型,但如果你这样做并且表现不佳,那是你的问题而不是 TypeScript 的问题。
这是一个这样的技巧。是 not supported:
type EndsWith<T extends MU, M extends MU> =
T extends M ? any :
{
base: never,
step: EndsWith<T extends any[] ? T[1] : never, M>
}[T extends '.' ? "base" : "step"];
我们所做的是将明显的循环类型转变为递归的树状对象,我们立即使用延迟条件类型对其进行索引。这仍然是循环,但编译器没有注意到它。
它"works":
declare const a: EndsWith<['U', ['I', '.']], ['I', '.']> // any
declare const b: EndsWith<['U', '.'], ['I', '.']> // never
但很脆弱。如果你这样写:
declare const c: EndsWith<MU, ['I', MU]> // oops
您要求编译器通过首先检查是否 MU extends ['I', MU]
(不是)来评估 EndsWith<MU, ['I', MU]>
,然后再次递归到... EndsWith<MU, ['I', MU]>
。呃哦。某个地方会有编译器问题;很可能你会得到一个“这种类型实例化太深的错误。如果你真的很聪明,你可以使用上面的 EndsWith
编写一些代码,这实际上会导致编译器挂起而不是吐出错误.
我觉得你只是为了学术目的而写 EndsWith
(因为作为 cons-like 对实现的列表不是惯用的 JS 或 TS)所以它不受支持的事实可能不是问题。如果我错了,而你想在某处使用它作为生产代码,请不要。我不想成为针对编译器犯罪的从犯。
希望对您有所帮助;祝你好运! Playground link to code
我在 Typescript 中有以下类型定义:
type MU = ['I' | 'U' | 'M', MU] | '.'
我正在尝试编写助手:
type EndsWith<T extends MU, M extends MU> = T extends M ? any : T extends [infer _, infer Q] ? EndsWith<Q, M> : never
这将检查特定类型 T
是否以给定的 M
、 结尾,即 :
const a: EndsWith<['U', ['I', '.']], ['I', '.']> // types as any
const b: EndsWith<['U', '.'], ['I', '.']> // types as never
但是,EndsWith
不是我指定的别名类型。寻找解决方法:)
目前不支持循环条件类型。请参阅 microsoft/TypeScript#26980 以了解建议解除此约束的未决问题。目前,它们是被禁止的。
请注意,很可能 欺骗 编译器评估循环条件类型,但如果你这样做并且表现不佳,那是你的问题而不是 TypeScript 的问题。
这是一个这样的技巧。是 not supported:
type EndsWith<T extends MU, M extends MU> =
T extends M ? any :
{
base: never,
step: EndsWith<T extends any[] ? T[1] : never, M>
}[T extends '.' ? "base" : "step"];
我们所做的是将明显的循环类型转变为递归的树状对象,我们立即使用延迟条件类型对其进行索引。这仍然是循环,但编译器没有注意到它。
它"works":
declare const a: EndsWith<['U', ['I', '.']], ['I', '.']> // any
declare const b: EndsWith<['U', '.'], ['I', '.']> // never
但很脆弱。如果你这样写:
declare const c: EndsWith<MU, ['I', MU]> // oops
您要求编译器通过首先检查是否 MU extends ['I', MU]
(不是)来评估 EndsWith<MU, ['I', MU]>
,然后再次递归到... EndsWith<MU, ['I', MU]>
。呃哦。某个地方会有编译器问题;很可能你会得到一个“这种类型实例化太深的错误。如果你真的很聪明,你可以使用上面的 EndsWith
编写一些代码,这实际上会导致编译器挂起而不是吐出错误.
我觉得你只是为了学术目的而写 EndsWith
(因为作为 cons-like 对实现的列表不是惯用的 JS 或 TS)所以它不受支持的事实可能不是问题。如果我错了,而你想在某处使用它作为生产代码,请不要。我不想成为针对编译器犯罪的从犯。
希望对您有所帮助;祝你好运! Playground link to code