You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何一次性修改对象的多个函数?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
    });
  };
});

代码说明

  1. 精确限定方法列表:通过Pick<Logger, ...>筛选出需要修改的方法名,确保methodsToWrap中的每个元素都是Logger中符合目标函数类型的key,TypeScript可准确推断每个方法的类型。
  2. 直接修改新对象:先复制原Logger对象,再逐个替换目标方法,避免解构重组带来的类型冲突。
  3. 类型安全的方法替换:由于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 14:25:44