TypeScript实现任意类型单值通用map转换函数(参考Kotlin作用域函数)
TypeScript 通用单值映射函数实现
针对你想要实现“任意单值通过独立作用域完成转换、复用通用变量命名”的需求,有两种成熟可落地的实现方式,完全匹配你参考的Kotlin作用域函数的行为:
方案1:原型扩展实现(完全匹配你期望的链式调用写法)
这是和你举的变量.map(转换函数)写法完全一致的实现方式,核心逻辑是给所有JS类型的根原型Object.prototype挂载通用的map方法,再补全对应的TS类型声明,即可实现任意类型值直接链式调用映射方法的效果。
实现代码
首先是TS全局类型声明,让TypeScript能识别到这个新增的方法,避免类型报错:
// global.d.ts declare global { interface Object { /** * 单值通用映射:将当前值作为入参传入转换函数,返回转换后的结果 * @param transform 接收当前值的转换函数 */ map<T, K>(this: T, transform: (value: T) => K): K } } export {}
然后是原型方法的实际挂载逻辑,放在项目入口文件执行一次即可:
// main.ts 或 全局初始化文件 if (!Object.prototype.map) { Object.prototype.map = function<T, K>(this: T, transform: (value: T) => K): K { return transform(this) } }
核心特性
- 类型推导完全正常:转换函数的入参会自动匹配调用map的变量原始类型(字符串、数字等原始值会自动去掉装箱包装,不会被识别成包装对象),返回值会自动推导为转换后的类型
- 不会覆盖原生数组的
map方法:原型链查找顺序为实例 ->Array.prototype->Object.prototype,数组调用map时会优先走原生数组的映射逻辑,和单值map的行为完全隔离 - 完全满足作用域隔离需求:传入的转换函数是独立的函数作用域,内部声明的中间变量(比如格式化日期时用到的
date、str)不会泄漏到外部作用域,多个转换场景可以复用相同的变量命名,不用为每个场景设计差异化变量名
可选增强:空值兼容
如果需要兼容null/undefined的场景,可以在实现里增加判空逻辑,避免调用时直接抛错:
if (!Object.prototype.map) { Object.prototype.map = function<T, K>(this: T, transform: (value: T) => K): K | undefined { if (this === null || this === undefined) return undefined return transform(this) } }
实际使用效果
实现完成后,你之前写的日期转换代码可以直接正常运行,类型检查完全通过:
data['other']['invoiceDateDotFormat'] = data['invoice']['invoiceDate'].map( (dateString) => { const date = new Date(dateString) let str = ('0' + date.getDate()).slice(-2) str += '.' + ('0' + (date.getMonth() + 1)).slice(-2) str += '.' + date.getFullYear() return str }, ) data['other']['purchaseDateDashFormat'] = data['invoice']['purchaseDateDashFormat'].map( (dateString) => { const date = new Date(dateString) let str = ('0' + date.getDate()).slice(-2) str += '-' + ('0' + (date.getMonth() + 1)).slice(-2) str += '-' + date.getFullYear() return str }, )
方案2:无原型扩展的纯函数实现(无全局污染,更安全)
如果担心扩展原生原型带来的潜在冲突(比如和第三方库的原型枚举逻辑不兼容、ESLint的no-extend-native规则报错),可以实现独立的泛型纯函数,虽然写法上没有链式调用简洁,但完全没有全局副作用:
/** * 通用单值映射函数 * @param value 待转换的原始值 * @param transform 转换逻辑函数 */ function mapValue<T, K>(value: T, transform: (value: T) => K): K { return transform(value) }
实际使用效果
data['other']['invoiceDateDotFormat'] = mapValue( data['invoice']['invoiceDate'], (dateString) => { const date = new Date(dateString) let str = ('0' + date.getDate()).slice(-2) str += '.' + ('0' + (date.getMonth() + 1)).slice(-2) str += '.' + date.getFullYear() return str }, )
这个实现和Kotlin的let作用域函数行为完全一致,同样能实现临时作用域隔离、复用通用变量命名的核心需求。
注意事项
- 原型扩展方案需要在项目的ESLint配置中为
Object.prototype.map开放白名单,否则会触发禁止修改原生原型的规则报错- 给原型挂载方法时不要将方法设置为可枚举属性,否则会影响原生
for...in循环的遍历结果
内容的提问来源于stack exchange,提问作者kirtan403
相关产品推荐
相关产品推荐

