如何定义字段级泛型的TypeScript类型SomeType?
问题描述
现有如下TypeScript类型定义:
type SomeType<Prop, Model> = { [value: string]: { loadMany: (ids: readonly Prop[]) => readonly Model[]; getProp: (ids: readonly Prop[]) => readonly Model[] } }
需要让Prop和Model泛型参数作用于字段级别而非SomeType类型级别,即实现如下结构:
class User { constructor(public id: number, public username: string) {} } const args: SomeType = { id: { loadMany: (ids: readonly number[]) => new Array(ids.length).fill(new User(0, 'sup')), getProp: (user: User) => user.id, }, username: { loadMany: (ids: readonly string[]) => new Array(ids.length).fill(new User(0, 'sup')), getProp: (user: User) => user.username, } }
请重新定义SomeType以满足需求。
解决方案
根据需求,我们可以通过映射类型结合泛型约束实现字段级别的泛型控制,以下是几种不同场景的最优实现:
方案1:统一Model的严格约束(推荐)
如果所有字段的Model是同一个类型(比如示例中的User),可以基于Model的字段结构自动推导类型,确保loadMany的入参类型与getProp的返回值类型完全匹配:
// 单个字段的配置类型 type FieldConfig<Model, Prop> = { loadMany: (ids: readonly Prop[]) => readonly Model[]; getProp: (model: Model) => Prop; } // 基于Model生成字段映射的SomeType type SomeType<Model> = { [K in keyof Model]?: FieldConfig<Model, Model[K]>; }
使用示例:
class User { constructor(public id: number, public username: string) {} } const args: SomeType<User> = { id: { loadMany: (ids: readonly number[]) => new Array(ids.length).fill(new User(0, 'sup')), getProp: (user) => user.id, // 自动推导返回值为number }, username: { loadMany: (ids: readonly string[]) => new Array(ids.length).fill(new User(0, 'sup')), getProp: (user) => user.username, // 自动推导返回值为string } };
这种方案的类型安全性最高,TypeScript会自动校验每个字段的类型一致性。
方案2:支持多Model的灵活配置
如果不同字段的Model类型不同,可以通过泛型对象定义每个字段的Prop和Model,再通过映射类型生成最终结构:
type FieldConfig<Prop, Model> = { loadMany: (ids: readonly Prop[]) => readonly Model[]; getProp: (model: Model) => Prop; } type SomeType<FieldMap extends Record<string, { Prop: any; Model: any }>> = { [K in keyof FieldMap]: FieldConfig<FieldMap[K]['Prop'], FieldMap[K]['Model']>; }
使用示例:
class User { constructor(public id: number, public username: string) {} } class Post { constructor(public slug: string, public title: string) {} } // 显式指定每个字段的Prop和Model类型 const args = { userId: { loadMany: (ids: readonly number[]) => new Array(ids.length).fill(new User(0, 'sup')), getProp: (user: User) => user.id, }, postSlug: { loadMany: (slugs: readonly string[]) => new Array(slugs.length).fill(new Post('test', 'Hello')), getProp: (post: Post) => post.slug, } } satisfies SomeType<{ userId: { Prop: number; Model: User }; postSlug: { Prop: string; Model: Post }; }>;
方案3:无预定义的松散约束
如果不需要严格的类型校验,仅需要支持字段级别的泛型参数,可以直接在方法上定义泛型:
type SomeType = { [key: string]: { loadMany: <Prop, Model>(ids: readonly Prop[]) => readonly Model[]; getProp: <Prop, Model>(model: Model) => Prop; }; }
这种方式灵活性最高,但缺乏类型校验,适合快速原型开发场景。
内容的提问来源于stack exchange,提问作者Albina
相关产品推荐
相关产品推荐

