typescript - 为什么在循环依赖中,命名函数的处理方式与箭头函数不同?
问题描述
我正在尝试在 TypeScript 中推出自己的依赖注入机制,但遇到了一个有趣的问题。在声明我的依赖项时,我必须使用命名函数而不是箭头函数(通常是我的默认函数),否则我会收到循环声明错误。
以下是构成我的依赖解析器的一组类型:
type CollectionTemplate = {
[key: string]: (...args: any) => any
}
type RegistryTemplate = {
[key: string]: CollectionTemplate
}
type RegistryCollection<R extends RegistryTemplate> = keyof R
type CollectionEntry<C extends CollectionTemplate> = keyof C
type BaseRegistry<R extends RegistryTemplate> = R
type BaseContext<R extends RegistryTemplate> = {
[C in RegistryCollection<R>]: {
[E in CollectionEntry<R[C]>]: ReturnType<R[C][E]>
}
}
type BaseUseDep<R extends RegistryTemplate> =
<C extends RegistryCollection<R>, E extends CollectionEntry<R[C]>>
(collection: C, entry: E) => BaseContext<R>[C][E]
现在,当我使用命名函数时,这一切都很好:
type DepRegistry = BaseRegistry<typeof registry>
type DepContext = BaseContext<DepRegistry>
type UseDep = BaseUseDep<DepRegistry>
function greet(deps: UseDep)
{
return (name: string) =>
{
const console = deps('system', 'console')
console.log(name)
}
}
function printGreeting(deps: UseDep)
{
return () =>
{
const greet = deps('social', 'greet')
greet('John')
}
}
const registry = {
system: {
console: () => console,
},
social: {
greet,
printGreeting,
}
}
但是,如果我使用箭头函数而不是命名函数声明这些函数,则会出现以下错误:
Type alias 'DepRegistry' circularly references itself. ts(2456)
Type alias 'UseDep' circularly references itself. ts(2456)
为什么是这样?仅仅是因为我将函数分配给变量而不是声明显式函数吗?
解决方案
以下答案部分解释了该问题;它的意思是评论
我认为,命名函数和箭头函数之间存在差异的原因可能是类型签名的lazy vs eager
解析。
Anders Hejlsberginterface vs type
在此 GitHub 评论中比较了分辨率:
诀窍是在接口类型中进行递归反向引用。这是因为接口基类型和接口成员的解析被延迟,而类型别名的解析被急切地执行。
function
可以指定 a 的签名,interface { (...params: Params): Return }
因此 Typescript 编译器使用惰性解析策略,并且允许递归定义。
我不知道为什么箭头函数被区别对待,但似乎他们的类型签名被热切地评估了。
我经常遵循类似的依赖注册/注入模式,之前遇到过这个问题。从来没有想过用常规功能替换箭头。很高兴知道这是一种可能性。您也可以首先考虑避免循环依赖。
这个问题本身非常违反直觉,同时也非常有趣。
推荐阅读
- verilog - 我无法将输出分配给 Verilog 中的值
- c# - Microsoft Graph .NET SDK - 检索 ListItem 字段
- excel - 使用左函数作为循环的一部分 - VBA
- openwrt - 在 openwrt 上设置强制门户
- javascript - Redux-React 通过 mapDispatchToProps 方法传递对象
- http - Dart 中 http 和 HttpClient 的区别
- android - 使 Android 布局独立于系统区域设置?
- html - cpuinfo 的 BASH 格式化输出
- http - 什么是 www10 地址?
- c# - 在 C# 中将长字符串转换为双精度