TypeScript如何为泛型对象的每个键实现类型推断与约束
TypeScript泛型配置类型的类型推断实现问题
首先定义了如下泛型类型:
type Value<T = any> = { component: ComponentType<T>, getDuration: (input: T) => void }
原本的配置类型定义为:
type ConfigType = { [k: string]: Value }
需求如下:
- 为每个配置项添加类型约束,禁止创建无效配置:比如
component接收Prop1类型参数,但getDuration接收Prop2类型参数(Prop1≠Prop2)的情况必须报错;两者参数类型一致的配置则有效。 - 通过键访问配置时,TypeScript需要自动推断出具体的泛型类型,而非
any。例如config.Default.component要推断为ComponentType<Prop1>,而非ComponentType<any>。
曾尝试以下写法但无效:
type ConfigTemplates<Config extends { [k: string]: Value<unknown> }> = Config extends Record<string, Value> ? { [templateName in string]: Config[templateName] extends Value< infer Props > ? Value<Props> : never; } : never;
解决方案
方案1:泛型配置类型+satisfies关键字
让ConfigType成为泛型类型,为每个键单独绑定对应的泛型参数,再通过satisfies约束配置结构:
import type { ComponentType } from 'react'; // 假设ComponentType来自React type Value<T = any> = { component: ComponentType<T>, getDuration: (input: T) => void }; // 泛型配置类型,每个键对应专属的Value<T> type ConfigType<T extends Record<string, unknown>> = { [K in keyof T]: Value<T[K]> }; // 示例Props类型 type Prop1 = { id: number }; type Prop2 = { name: string }; type Prop3 = { age: number }; // 创建配置,自动推导类型,不匹配会报错 const config = { Default: { component: (props: Prop1) => null, getDuration: (props: Prop2) => {} // ❌ 类型不匹配,TypeScript报错 }, Template1: { component: (props: Prop3) => null, getDuration: (props: Prop3) => {} // ✅ 类型匹配,正常通过 } } satisfies ConfigType<{ Default: Prop1, Template1: Prop3 }>; // 类型推断正常 const defaultComp = config.Default.component; // 类型为ComponentType<Prop1> const template1GetDuration = config.Template1.getDuration; // 类型为(input: Prop3) => void
方案2:工具函数自动推导
如果不想手动声明泛型参数,可以用工具函数来自动推导并约束类型:
import type { ComponentType } from 'react'; type Value<T = any> = { component: ComponentType<T>, getDuration: (input: T) => void }; // 工具函数,用于约束配置结构并自动推导类型 function createConfig<T extends Record<string, unknown>>(config: { [K in keyof T]: Value<T[K]> }) { return config; } // 示例Props类型 type Prop1 = { id: number }; type Prop2 = { name: string }; type Prop3 = { age: number }; // 使用工具函数创建配置 const config = createConfig({ Default: { component: (props: Prop1) => null, getDuration: (props: Prop2) => {} // ❌ 类型不匹配,直接报错 }, Template1: { component: (props: Prop3) => null, getDuration: (props: Prop3) => {} // ✅ 类型匹配 } }); // 自动推断出具体类型 config.Default.component; // ComponentType<Prop1> config.Template1.getDuration; // (input: Prop3) => void
原写法无效的原因
你之前的ConfigTemplates类型通过条件类型试图推断参数,但它的索引签名是[templateName in string],会丢失具体键名和对应泛型参数的关联信息,最终只能得到一个模糊的string索引类型,无法实现精确的类型约束和推断。
内容的提问来源于stack exchange,提问作者Vladimir Topolev
相关产品推荐
相关产品推荐

