TypeScript中'{ [key in K]: unknown; } & S["data"]'类型赋值错误修复咨询
问题场景
以下代码中setPropertyValueOnScope和getPropertyValueFromScope两个函数存在类型不匹配错误:
export enum Scope { Form = "form", Nest = "nest", } export type ScopeKeyListType< L extends Scope, T extends ScopeType<L>, > = T['parent'] extends ScopeType<infer A extends Scope> ? keyof T['data'] & ScopeKeyListType<A, T['parent']> : keyof T['data'] export type ScopeNestDataType = { index: number; nest: { foo: string; bar: boolean; }; }; export type ScopeFormDataType = { hello: string; world: number; }; export type ScopeTableType = { form: ScopeFormDataType; nest: ScopeNestDataType; }; export type ScopeType< A extends Scope, B extends unknown = unknown > = { data: ScopeTableType[A]; like: A; parent?: B extends ScopeType<infer T extends Scope, infer Q> ? B : never }; export type ScopeValueType< L extends Scope, T extends ScopeType<L>, K extends ScopeKeyListType<L, T>, > = T['data'] extends { [key in K]: unknown } ? T['data'][K] : T['parent'] extends ScopeType<infer A extends Scope> ? ScopeValueType< A, T['parent'], ScopeKeyListType<A, T['parent']> > : never export type ParentScopeType<S> = | S | (S extends ScopeType<infer X, infer Y> ? Y extends ScopeType<infer A, infer B> ? ParentScopeType<Y> : never : never) export function setPropertyValueOnScope< L extends Scope, S extends ScopeType<L>, K extends ScopeKeyListType<L, S> >(scope: S, property: K, value: ScopeValueType<L, S, K>): void { if (property in scope.data) { scope.data[property] = value; } else if (scope.parent) { setPropertyValueOnScope(scope.parent, property, value); } else { throw new Error(`Property not defined on scope`); } } export function getPropertyValueFromScope< L extends Scope, S extends ScopeType<L>, K extends ScopeKeyListType<L, S>, >(scope: S, path: K): ScopeValueType<L, S, K> | undefined { let source: ParentScopeType<S> | undefined = scope while (source) { if (path in source.data) { break } else { source = source.parent } } if (!source) { return } return source.data[path] }
错误信息
第一个错误(setPropertyValueOnScope函数)
Type 'ScopeValueType<L, S, K>' is not assignable to type 'ScopeTableType[L][K]'.
Type 'S["data"][K] | (S["parent"] extends ScopeType<infer A extends Scope, unknown> ? ScopeValueType<A, S["parent"], ScopeKeyListType<A, S["parent"]>> : never)' is not assignable to type 'ScopeTableType[L][K]'.
Type 'S["data"][K]' is not assignable to type 'ScopeTableType[L][K]'.
Type '{ [key in K]: unknown; } & S["data"]' is not assignable to type 'ScopeTableType[L]'.
Type '{ [key in K]: unknown; } & S["data"]' is not assignable to type 'ScopeFormDataType & ScopeNestDataType'.
Type '{ [key in K]: unknown; } & S["data"]' is not assignable to type 'ScopeFormDataType'.(2322)
第二个错误(getPropertyValueFromScope函数)
Type 'ScopeTableType[L][K]' is not assignable to type 'ScopeValueType<L, S, K> | undefined'.
Type 'ScopeTableType[L][ScopeKeyListType<L, S>]' is not assignable to type 'ScopeValueType<L, S, K> | undefined'.
Type 'ScopeTableType[L][keyof S["data"]] | ScopeTableType[L][keyof S["data"] & ScopeKeyListType<Scope, S["parent"]>]' is not assignable to type 'ScopeValueType<L, S, K> | undefined'.
Type 'ScopeTableType[L][keyof S["data"]]' is not assignable to type 'ScopeValueType<L, S, K> | undefined'.
Type 'ScopeTableType[L][string] | ScopeTableType[L][number] | ScopeTableType[L][symbol]' is not assignable to type 'ScopeValueType<L, S, K> | undefined'.
Type 'ScopeTableType[L][string]' is not assignable to type 'ScopeValueType<L, S, K> | undefined'.(2322)
修复方案
核心问题在于TypeScript无法自动推导嵌套Scope中属性的精确类型关系,需要通过类型断言和优化递归类型定义来解决:
1. 优化ScopeType与ScopeValueType定义
简化泛型逻辑,让类型推导更清晰:
export type ScopeType< A extends Scope, B extends ScopeType<Scope> | undefined = undefined > = { data: ScopeTableType[A]; like: A; parent?: B; }; export type ScopeValueType< T extends ScopeType<Scope>, K extends ScopeKeyListType<Scope, T>, > = K extends keyof T['data'] ? T['data'][K] : T['parent'] extends ScopeType<infer A extends Scope> ? ScopeValueType<T['parent'], K & ScopeKeyListType<A, T['parent']>> : never
2. 修复setPropertyValueOnScope函数
通过类型断言明确赋值的类型匹配关系:
export function setPropertyValueOnScope< T extends ScopeType<Scope>, K extends ScopeKeyListType<Scope, T> >(scope: T, property: K, value: ScopeValueType<T, K>): void { if (property in scope.data) { (scope.data as Record<K, ScopeValueType<T, K>>)[property] = value; } else if (scope.parent) { setPropertyValueOnScope(scope.parent, property, value as ScopeValueType<typeof scope.parent, K>); } else { throw new Error(`Property not defined on scope`); } }
3. 修复getPropertyValueFromScope函数
添加类型断言确保返回值类型匹配:
export function getPropertyValueFromScope< T extends ScopeType<Scope>, K extends ScopeKeyListType<Scope, T>, >(scope: T, path: K): ScopeValueType<T, K> | undefined { let source: ParentScopeType<T> | undefined = scope while (source) { if (path in source.data) { break } else { source = source.parent as ParentScopeType<T> | undefined; } } if (!source) { return } return source.data[path] as ScopeValueType<T, K>; }
完整修复代码
export enum Scope { Form = "form", Nest = "nest", } export type ScopeKeyListType< L extends Scope, T extends ScopeType<L>, > = T['parent'] extends ScopeType<infer A extends Scope> ? keyof T['data'] & ScopeKeyListType<A, T['parent']> : keyof T['data'] export type ScopeNestDataType = { index: number; nest: { foo: string; bar: boolean; }; }; export type ScopeFormDataType = { hello: string; world: number; }; export type ScopeTableType = { form: ScopeFormDataType; nest: ScopeNestDataType; }; export type ScopeType< A extends Scope, B extends ScopeType<Scope> | undefined = undefined > = { data: ScopeTableType[A]; like: A; parent?: B; }; export type ScopeValueType< T extends ScopeType<Scope>, K extends ScopeKeyListType<Scope, T>, > = K extends keyof T['data'] ? T['data'][K] : T['parent'] extends ScopeType<infer A extends Scope> ? ScopeValueType<T['parent'], K & ScopeKeyListType<A, T['parent']>> : never export type ParentScopeType<S> = | S | (S extends ScopeType<infer X, infer Y> ? Y extends ScopeType<infer A, infer B> ? ParentScopeType<Y> : never : never) export function setPropertyValueOnScope< T extends ScopeType<Scope>, K extends ScopeKeyListType<Scope, T> >(scope: T, property: K, value: ScopeValueType<T, K>): void { if (property in scope.data) { (scope.data as Record<K, ScopeValueType<T, K>>)[property] = value; } else if (scope.parent) { setPropertyValueOnScope(scope.parent, property, value as ScopeValueType<typeof scope.parent, K>); } else { throw new Error(`Property not defined on scope`); } } export function getPropertyValueFromScope< T extends ScopeType<Scope>, K extends ScopeKeyListType<Scope, T>, >(scope: T, path: K): ScopeValueType<T, K> | undefined { let source: ParentScopeType<T> | undefined = scope while (source) { if (path in source.data) { break } else { source = source.parent as ParentScopeType<T> | undefined; } } if (!source) { return } return source.data[path] as ScopeValueType<T, K>; }
修复说明
- 简化
ScopeType的泛型定义,明确parent的类型范围,避免复杂条件判断导致的推导混乱。 - 调整
ScopeValueType的递归逻辑,优先判断属性是否属于当前Scope的data,提升类型推导效率。 - 通过类型断言告知编译器属性与值的类型匹配关系,解决TypeScript无法自动推导嵌套类型的问题。
内容的提问来源于stack exchange,提问作者Lance Pollard

