如何在TypeScript中创建无类型断言的子类型通用转换器函数
解决TypeScript泛型转换器的类型问题
一、无类型断言的泛型Cat扩展函数
你之前的代码报错,核心原因是TypeScript无法保证{ ...originalObject, ...extension }完全符合任意E extends Cat的结构——因为E可能包含额外的隐含属性或更严格的类型约束。调整泛型定义,明确扩展属性的类型,就能避免这个问题:
type Cat = { whiskers: number; } type FancyCat = Cat & { tophatSize: number; } type PartyCat = Cat & { balloons: number; } // 泛型函数:接收扩展属性,返回将Cat转换为「Cat+扩展」类型的转换器 const createCatExtender = <Ext extends object>(extension: Ext) => (originalCat: Cat): Cat & Ext => { return { ...originalCat, ...extension }; }; // 调用示例:无需显式指定泛型,TypeScript自动推断 const toPartyCat = createCatExtender({ balloons: 7 }); const myPartyCat = toPartyCat({ whiskers: 5 }); // 类型自动推导为与PartyCat兼容的Cat & { balloons: number } // 如果需要严格返回指定子类型,可显式声明: const toFancyCat = createCatExtender<{ tophatSize: number }>({ tophatSize: 3 }); const myFancyCat: FancyCat = toFancyCat({ whiskers: 2 }); // 完全兼容,无类型错误
这个版本通过将泛型参数聚焦在扩展属性的类型上,让TypeScript明确知道返回值是Cat与扩展属性的交集,彻底消除了类型不匹配的报错,且无需任何类型断言。
二、通用对象属性更新转换器
如果要构建更通用的、可用于任意对象的属性更新转换器,结合高阶函数和pipe可以实现高表现力的逻辑组合:
1. 通用属性更新函数
// 接收属性名和更新逻辑,返回对应的对象转换器 const updateProp = <T, K extends keyof T>(key: K, updater: (currentValue: T[K]) => T[K]) => (obj: T): T => ({ ...obj, [key]: updater(obj[key]) });
2. 自定义pipe函数(不依赖第三方库)
// 实现基础的pipe:按顺序执行多个函数,将前一个的输出作为后一个的输入 const pipe = <T>(initialValue: T) => <F extends Array<(arg: any) => any>>( ...fns: F ): ReturnType<F[number]> => fns.reduce((acc, fn) => fn(acc), initialValue);
3. 组合使用示例
// 组合「扩展Cat为PartyCat」+「翻倍气球数量」+「增加胡须数量」 const transformToSuperPartyCat = pipe({ whiskers: 3 }) ( createCatExtender({ balloons: 5 }), updateProp('balloons', count => count * 2), updateProp('whiskers', count => count + 1) ); // 结果:{ whiskers: 4, balloons: 10 },类型自动推导为Cat & { balloons: number }
这种方式完全遵循函数式编程风格,每个转换器都是纯函数,可独立复用、组合,同时保持严格的类型安全。
内容的提问来源于stack exchange,提问作者Nick Manning
相关产品推荐
相关产品推荐

