如何一次性修改对象的多个函数?TypeScript类型问题求助
解决Logger接口方法包装的TypeScript类型错误
问题背景
我们有如下Logger接口:
export declare interface Logger { info(msg: string, options?: LogOptions): void; warn(msg: string, options?: LogOptions): void; warnOnce(msg: string, options?: LogOptions): void; error(msg: string, options?: LogOptions): void; clearScreen(type: LogType): void; hasErrorLogged(error: Error | RollupError): boolean; hasWarned: boolean; }
需要创建一个结构完全一致的新Logger对象,统一修改info、warn、warnOnce和error这四个方法的逻辑(替换消息中的"A"为"B",并合并自定义options),但原实现出现TypeScript错误:Element implicitly has an 'any' type because expression of type 'string | number | symbol' can't be used to index type。
错误原因
原代码中使用Object.keys(functionsToAlter)遍历得到的key是string类型,即便断言为keyof Partial<Logger>,TypeScript仍无法确认这些key对应的属性是符合目标函数类型的方法,导致类型推断失败。
正确实现
我们可以通过显式指定需要包装的方法名列表,利用TypeScript的类型系统保证类型安全,避免Object.keys带来的类型不确定性:
const alterMsg = (msg: string) => msg.replaceAll('A', 'B'); const alterOptions = { flag: false, }; // 明确指定需要包装的方法名,限定为Logger中对应方法的key类型 const methodsToWrap: Array<keyof Pick<Logger, 'info' | 'warn' | 'warnOnce' | 'error'>> = [ 'info', 'warn', 'warnOnce', 'error' ]; const logger: Logger = createLogger(); // 先复制原Logger的所有属性,得到结构完整的新对象 const newLogger: Logger = { ...logger }; // 逐个包装目标方法 methodsToWrap.forEach(method => { const original = newLogger[method]; newLogger[method] = (msg: string, options?: LogOptions) => { original(alterMsg(msg), { ...alterOptions, ...options }); }; });
代码说明
- 精确限定方法列表:通过
Pick<Logger, ...>筛选出需要修改的方法名,确保methodsToWrap中的每个元素都是Logger中符合目标函数类型的key,TypeScript可准确推断每个方法的类型。 - 直接修改新对象:先复制原Logger对象,再逐个替换目标方法,避免解构重组带来的类型冲突。
- 类型安全的方法替换:由于
method是明确的key类型,newLogger[method]会被推断为对应的函数类型,替换后的函数严格遵循原方法的参数和返回值类型,保证类型一致性。
简洁实现方式(利用泛型封装)
如果希望代码更紧凑,可通过泛型函数封装统一修改逻辑,再逐个应用到目标方法:
const alterMsg = (msg: string) => msg.replaceAll('A', 'B'); const alterOptions = { flag: false, }; const logger: Logger = createLogger(); // 封装统一的方法包装逻辑 const wrapMethod = <T extends (msg: string, options?: LogOptions) => void>(original: T) => { return (msg: string, options?: LogOptions) => { original(alterMsg(msg), { ...alterOptions, ...options }); }; }; // 扩展原对象并替换目标方法 const newLogger: Logger = { ...logger, info: wrapMethod(logger.info), warn: wrapMethod(logger.warn), warnOnce: wrapMethod(logger.warnOnce), error: wrapMethod(logger.error), };
内容的提问来源于stack exchange,提问作者leonheess
相关产品推荐
相关产品推荐

