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

如何在TypeScript中通过对象属性动态调用类方法?

解决菜单逻辑机器人的TypeScript类型错误问题

核心问题分析

你遇到的两个类型错误本质是TypeScript类型检查对动态方法调用的约束:

  1. processUserInput返回值可能为undefined,直接用result?.method作为索引会触发undefined不能作为索引的错误;
  2. menuItem.method被推断为宽泛的string类型,TypeScript不允许用任意字符串索引类实例(除非显式声明索引签名)。

解决方案:严格类型约束 + 类型缩小

步骤1:定义精确的类型别名

先给菜单配置和方法名称做严格的类型绑定,确保method只能是BotMessageController中存在的方法名:

// 提取BotMessageController的所有方法名类型
type BotControllerMethod = keyof BotMessageController;

// 定义单个菜单的配置类型
type MenuConfig = {
  method: BotControllerMethod;
  toLowerCase?: boolean;
  removeAccents?: boolean;
  removeSpecialChars?: boolean;
  replaceSpacesWithUnderscores?: boolean;
};

// 定义菜单映射的类型,键是有效的menuIndex字符串
type MenuIndexMapping = Record<string, MenuConfig>;

步骤2:优化processUserInput函数

调整函数返回类型,通过类型守卫确保有效分支返回非undefined值,用never处理无效分支:

function processUserInput(input: string, menuIndex: string): { processedUserMessage: string; method: BotControllerMethod } | never {
  const menuIndexMapping: MenuIndexMapping = {
    starting: {
      method: "processUserOptions",
      toLowerCase: true,
      removeAccents: true,
      removeSpecialChars: true,
      replaceSpacesWithUnderscores: true,
    },
    // 其他菜单配置...
  };

  const menuItem = menuIndexMapping[menuIndex];

  if (menuItem) {
    let processedUserMessage = input;

    if (menuItem.toLowerCase) {
      processedUserMessage = processedUserMessage.toLowerCase();
    }
    // 执行其他处理逻辑...

    return { processedUserMessage, method: menuItem.method };
  } else {
    console.log("Invalid menuIndex:", menuIndex);
    throw new Error(`Invalid menuIndex: ${menuIndex}`); // 抛出错误或返回never类型值
  }
}

步骤3:安全调用目标方法

现在processUserInput不会返回undefined,可以安全调用方法,TypeScript会自动校验method的合法性:

if (conversationStatus === "active") {
  try {
    const result = processUserInput(userMessage, menuIndex);
    // 这里method的类型是BotControllerMethod,TypeScript自动校验
    await this.botMessageController[result.method](
      result.processedUserMessage,
      userId,
      menuIndex,
      conversation2.id,
      errorsCounter
    );
  } catch (err) {
    // 处理无效menuIndex的情况
    console.error(err);
  }
} else {
  console.log("Bot muted");
  return;
}

替代方案:直接映射处理逻辑与方法

如果不想单独拆分输入处理函数,可以把每个菜单的处理逻辑直接绑定到方法映射中,进一步简化代码:

// 定义菜单处理映射,每个键对应处理函数和目标方法
const menuHandlers = {
  starting: {
    handler: (input: string) => input.toLowerCase()
      .normalize("NFD")
      .replace(/[\u0300-\u036f]/g, "")
      .replace(/[^a-zA-Z0-9\s_]/gi, "")
      .replace(/\s+/g, "_"),
    method: "processUserOptions" as const,
  },
  option_1: {
    handler: (input: string) => input.normalize("NFD")
      .replace(/[\u0300-\u036f]|[^a-zA-Z0-9\s_]/gi, "")
      .replace(/\s+/g, "_"),
    method: "handleOption1" as const,
  },
  registration_yes: {
    handler: (input: string) => input.normalize("NFD")
      .replace(/[\u0300-\u036f]|[^a-zA-Z0-9\s_]/gi, ""),
    method: "handleRegistrationYesOptions" as const,
  },
};

// 调用时直接使用
if (conversationStatus === "active") {
  const handler = menuHandlers[menuIndex as keyof typeof menuHandlers];
  if (handler) {
    const processedInput = handler.handler(userMessage);
    await this.botMessageController[handler.method](
      processedInput,
      userId,
      menuIndex,
      conversation2.id,
      errorsCounter
    );
  } else {
    console.log("Invalid menuIndex:", menuIndex);
  }
}

这种方式把输入处理和方法绑定直接放在同一配置中,逻辑更集中,同时通过as const让TypeScript推断出精确的方法名类型,避免类型错误。

内容的提问来源于stack exchange,提问作者JhonesJGL

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 07:37:35