映射到对象时TypeScript映射类型失效问题排查
问题:为何
DependencyRegistry的注册逻辑会抛出TypeScript错误? 我定义了描述Dependencies与Container的类型,希望为每个依赖注册对应的提供者函数。以下代码实现了两种“注册表”:
DependencyProviders:依赖名称与其提供者的映射DependencyRegistry:依赖名称与包含其提供者的对象的映射
代码实现如下:
interface Dependencies { a: string b: number c: boolean } type DependencyProviders = { [DepName in keyof Dependencies]: () => Dependencies[DepName] } type DependencyRegistry = { [DepName in keyof Dependencies]: { provider: () => Dependencies[DepName] } } class Container { private readonly providers: Partial<DependencyProviders> = {} private readonly registry: Partial<DependencyRegistry> = {} register<DepName extends keyof Dependencies>( name: DepName, provider: DependencyProviders[DepName] ) { this.providers[name] = provider this.registry[name] = { provider } // 此行抛出错误 } }
该行代码抛出的错误信息:
Type '{ provider: DependencyProviders[DepName]; }' is not assignable to type 'Partial<DependencyRegistry>[DepName]'. Type '{ provider: DependencyProviders[DepName]; }' is not assignable to type '{ provider: () => string; } & { provider: () => number; } & { provider: () => boolean; }'. Type '{ provider: DependencyProviders[DepName]; }' is not assignable to type '{ provider: () => string; }'. Types of property 'provider' are incompatible. Type 'DependencyProviders[DepName]' is not assignable to type '() => string'. Type '(() => string) | (() => number) | (() => boolean)' is not assignable to type '() => string'. Type '() => number' is not assignable to type '() => string'. Type 'number' is not assignable to type 'string'. Type '() => Dependencies[DepName]' is not assignable to type '() => string'. Type 'Dependencies[DepName]' is not assignable to type 'string'. Type 'string | number | boolean' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'.
原因分析
TypeScript的类型推断在这里出现了窄化失败:
- 对于
DependencyProviders,直接赋值this.providers[name] = provider可以正常工作,因为DependencyProviders[DepName]和Partial<DependencyProviders>[DepName]是精确匹配的——泛型DepName会严格约束提供者类型与依赖名称的对应关系。 - 但对于
DependencyRegistry,创建{ provider }时,TypeScript无法将DepName的泛型约束传递到这个对象的类型推断中,反而将其泛化为{ provider: DependencyProviders[keyof Dependencies] }(即提供者可能是任意类型的函数)。而Partial<DependencyRegistry>[DepName]要求的是与当前依赖完全匹配的提供者对象,因此TypeScript判定泛化后的对象无法满足类型要求,抛出不兼容错误。
修复方案
有两种可靠的修复方式:
方式一:显式类型断言
直接告知TypeScript,{ provider }就是当前DepName对应的注册表项类型:
this.registry[name] = { provider } as DependencyRegistry[DepName]
方式二:用辅助函数约束类型
通过一个辅助函数来生成符合要求的注册表项,利用函数参数的泛型约束让TypeScript正确推断类型:
private createRegistryEntry<DepName extends keyof Dependencies>( provider: DependencyProviders[DepName] ): DependencyRegistry[DepName] { return { provider } } // 在register方法中调用 this.registry[name] = this.createRegistryEntry(provider)
修复后的完整代码(方式二示例)
interface Dependencies { a: string b: number c: boolean } type DependencyProviders = { [DepName in keyof Dependencies]: () => Dependencies[DepName] } type DependencyRegistry = { [DepName in keyof Dependencies]: { provider: () => Dependencies[DepName] } } class Container { private readonly providers: Partial<DependencyProviders> = {} private readonly registry: Partial<DependencyRegistry> = {} private createRegistryEntry<DepName extends keyof Dependencies>( provider: DependencyProviders[DepName] ): DependencyRegistry[DepName] { return { provider } } register<DepName extends keyof Dependencies>( name: DepName, provider: DependencyProviders[DepName] ) { this.providers[name] = provider this.registry[name] = this.createRegistryEntry(provider) } }
内容的提问来源于stack exchange,提问作者kfiroo
相关产品推荐
相关产品推荐

