TypeScript通用工厂类型报错:createFeature函数类型定义问题
TypeScript简单工厂类型定义问题解决
问题背景
实现了一个简单工厂模式,但主函数createFeature处出现类型错误,需要修正类型定义。
代码示例
interface Feature { id: string; kind: string; } export function create<Kind extends string, Fields extends Record<any, any>>( kind: Kind, options: Fields, ): Feature & { kind: Kind } & Fields { return { id: '', kind, ...options, }; } interface PointOptions { coordinate: [number, number]; } interface Point extends Feature { x: number; y: number; } function createPoint({ coordinate: [x, y] }: PointOptions): Point { return create('point', { x, y }); } interface LineOptions { waypoints: [number, number][]; } interface Line extends Feature { coordinates: { x: number; y: number }[]; } function createLine(options: LineOptions): Line { return create('line', { coordinates: options.waypoints.map(([x, y]) => ({ x, y })), }); } const features = { point: createPoint, line: createLine, } as const; type Library = typeof features; export type FeatureKey = keyof Library; export type FeatureOptions<Type extends FeatureKey> = Library[Type] extends ( options: infer Options, ) => any ? Options : never; export type FeatureReturn<Type extends FeatureKey> = Library[Type] extends ( options: any, ) => infer ConcreteFeature ? ConcreteFeature : never; function createFeature<Type extends FeatureKey>( type: Type, options: FeatureOptions<Type>, ): FeatureReturn<Type> { if (!(type in features)) { throw new Error('Unknown feature type'); } return features[type](options); }
报错信息
Type 'Point | Line' is not assignable to type 'FeatureReturn<Type>'. Type 'Point' is not assignable to type 'FeatureReturn<Type>'. Argument of type 'FeatureOptions<Type>' is not assignable to parameter of type 'PointOptions & LineOptions'. Type 'unknown' is not assignable to type 'PointOptions & LineOptions'. Type 'unknown' is not assignable to type 'PointOptions'. Type 'FeatureOptions<Type>' is not assignable to type 'PointOptions'. Type 'unknown' is not assignable to type 'PointOptions'.
问题原因
当使用泛型Type extends FeatureKey时,TypeScript无法将type的具体值与options、返回值的类型做精确绑定。features[type]的类型是联合类型typeof createPoint | typeof createLine,调用该联合类型函数时,TypeScript会要求参数是所有函数参数的交集(即PointOptions & LineOptions,这显然不符合业务逻辑),返回值则是Point | Line,无法匹配FeatureReturn<Type>的具体类型。
解决方案
方案1:函数重载(最直观)
为每个Feature类型定义重载签名,明确参数与返回值的对应关系:
// 重载签名,明确每种类型的参数和返回值 function createFeature(type: 'point', options: PointOptions): Point; function createFeature(type: 'line', options: LineOptions): Line; // 实现签名 function createFeature(type: FeatureKey, options: FeatureOptions<FeatureKey>) { if (!(type in features)) { throw new Error('Unknown feature type'); } return features[type](options as any); }
方案2:泛型+类型断言(保留灵活性)
若需保留泛型的扩展能力,可通过类型断言告知TypeScript参数与返回值的正确性:
function createFeature<Type extends FeatureKey>( type: Type, options: FeatureOptions<Type>, ): FeatureReturn<Type> { if (!(type in features)) { throw new Error('Unknown feature type'); } // 断言参数匹配当前类型的函数参数,返回值匹配对应Feature类型 return features[type](options as Parameters<Library[Type]>[0]) as FeatureReturn<Type>; }
方案3:重构类型映射(优雅扩展)
定义完整的Feature类型映射,让TypeScript自动推导关联类型:
// 定义Feature类型映射,统一管理每种类型的参数和返回值 type FeatureMap = { point: { Options: PointOptions; Type: Point; }; line: { Options: LineOptions; Type: Line; }; }; // 基于映射推导全局类型 export type FeatureKey = keyof FeatureMap; export type FeatureOptions<Type extends FeatureKey> = FeatureMap[Type]['Options']; export type FeatureReturn<Type extends FeatureKey> = FeatureMap[Type]['Type']; // 保持原features对象不变 const features = { point: createPoint, line: createLine, } as const; function createFeature<Type extends FeatureKey>( type: Type, options: FeatureOptions<Type>, ): FeatureReturn<Type> { if (!(type in features)) { throw new Error('Unknown feature type'); } return features[type](options as any) as FeatureReturn<Type>; }
验证
修改后,调用createFeature('point', { coordinate: [1, 2] })会自动推断返回Point类型,调用createFeature('line', { waypoints: [[1,2],[3,4]] })会自动推断返回Line类型,且无类型错误。
内容的提问来源于stack exchange,提问作者Vitaliy Leonov
相关产品推荐
相关产品推荐

