Playground链接
我有这些一般性的定义:
type Module<P extends Payloads, C extends Children> = {
payloads: P;
children: C;
};
type Children = Record<string, any>; // some values will be nested Modules
type Payloads = Record<string, any>;
type ExtractPayloads<M extends Module<any, any>> = M extends Module<infer P, any> ? P : never;
type ExtractChildren<M extends Module<any, any>> = M extends Module<any, infer C> ? C : never;
基本上,模块是指定children
类型的类型,children
类型可以包含嵌套模块。
我有这样的类型,它可以根据模块的payload
类型生成Action
:
type ModuleRootActions<
MODULE extends Module<any, any>,
PAYLOADS = ExtractPayloads<MODULE>,
> = {
[NAME in keyof PAYLOADS]: {
type: NAME;
payload: PAYLOADS[NAME];
};
}[keyof PAYLOADS];
接下来,我有一个递归类型,它可以帮助我为Module树中的所有Module(例如,包括它的子模块和孙模块等)生成Action
:
type AllModuleActions<
MODULE extends Module<any, any>,
CHILDREN = ExtractChildren<MODULE>,
> =
| ModuleRootActions<MODULE>
| {
[KEY in keyof CHILDREN]: CHILDREN[KEY] extends Module<any, any>
? AllModuleActions<CHILDREN[KEY]>
: never;
}[keyof CHILDREN];
最后,我举几个具体的例子:
type C = Module<{
"incrementBy": number;
}, {}>;
type B = Module<{
"setIsSignedIn": boolean;
}, {
c: C;
}>;
type A = Module<{
"concat": string;
"setIsDarkMode": boolean;
}, {
b: B;
}>;
到目前为止,我的所有类型都是正确的--我已经手动验证了这一点。现在,我正在编写一个函数,它接受泛型Module
的Action
。我可以成功地定义这些类型:
type ConcreteAction<M extends Module<any, any>> = AllModuleActions<M>;
const concreteAction: ConcreteAction<A> = {
type: "concat",
payload: "str",
}
但是一旦我试图把它们放在泛型函数中,我就会得到标题中的错误。
const composedReducer = <MODULE extends Module<any, any>>(
action: AllModuleActions<MODULE>,
) => {
if (action) {
}
};
在Playground链接中,您会注意到action
有错误:"类型示例化太深,可能是无限的"。我认为发生这种情况是因为MODULE
类型是泛型的,并且在Module
定义中可能有循环,即使从语义上我知道它是一棵树。
我该如何修正这个错误?有没有办法告诉编译器这个图永远是一棵树,并且永远不包含无限循环?
5条答案
按热度按时间4dbbbstv1#
AllModuleActions<M>
类型是递归的,编译器不能很好地处理它,你是indexing任意深入到一个对象类型,一次产生一个大的联合类型;在here之前,当生成一个对象类型的所有虚线路径时,我遇到过这个问题(例如,{a: {b: string, c: number}}
变成"a.b" | "a.c"
)。当
M
是一个 * specific * 类型时,在计算AllModuleActions<M>
这样的值时,通常不会看到这个问题;但是当它是一个未指定的generic类型参数(或者一个依赖于这样的类型参数的类型)时,你可能会遇到麻烦。你可能会看到那个"过分深"的错误。更糟糕的是,编译器往往会陷入困境,CPU使用率激增,IDE速度变慢。我不知道为什么会发生这种情况。也许最好的建议是不要构建这样的类型。如果你必须这样做,那么我发现了一些方法来帮助你,但是它们并不是万无一失的:
有时候你可以通过将这些类型中的一个改写为分布式条件类型来使编译器 * 延迟 * 对它的求值。如果
M
是一个泛型类型参数,而AllModuleActions<M>
给你带来麻烦,那么M extends any ? AllModuleActions<M> : never
可能不会:如果这不起作用,你可以尝试显式地限制你的递归类型的深度,这样在默认情况下,事情只会下降三到四个层次:
这和你的类似,只是我们增加了一个
D
参数,它(默认)从4
开始,每次AllModuleActions
求值时减小(注意Prev[4]
是3
,Prev[3]
是2
),直到最后达到never
,递归类型跳出:这些变通方法对于特定的用例可能有帮助,也可能没有帮助,并且可能存在可观察到的副作用(例如,类型可能不相同;类型推断可能表现不同;显示的quickinfo可能是更复杂的),所以要小心!
Playground代码链接
dfuffjeb2#
确保您没有旧的Typescript版本,因为旧版本在类型推断方面存在一些bug
对于我来说,从
3.9
更新到4.6.3
修复了这个问题。mpgws1up3#
好吧,这个错误很常见,所以我不知道我的帖子是否真的会有帮助,但我在一个使用Typescript和vueI18n的Vue项目中遇到了这个问题。当我在 *.vue文件之外的实用程序代码中引用VueI18n.global.t方法时,编译器引发了这个错误。我使用了Vue3、Vuetify和vueI18n,我的文件看起来像这样
经过几个小时的努力,我发现了这个非常简单的例子here。所以我更新了我的文件,现在它编译正确了。
7uzetpgm4#
添加到tsconfig.json:
然后安装这个:
npm i -D @swc/core @swc/helpers再生器运行时
对我很有效。
7uhlpewt5#
可以通过检查递归类型中的类型是否为
any
来避免这个错误。例如,我们希望提取对象中使用的所有值。
见操场。
TypeScript抛出一个错误,因为
AllValues
类型被any
类型无限调用。为了避免它,在递归类型AllValues
中,我们应该为any
类型添加一个检查,如下所示:见操场。
让我们回到你的例子,你所要做的就是在类型
AllModuleActions
中添加一个any
的检查。见操场。
在前面,作为一种解决方案,您可以限制递归类型(代码为
type Prev = [never, 0, 1, 2, ...]
)的最大嵌套调用数,这是可行的,但在这种情况下,生成的类型声明变得非常庞大。