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

如何用TypeScript定义含指定终止类型的递归对象?

多语言嵌套文本对象的TypeScript类型定义问题

我需要定义一个支持灵活嵌套结构的多语言字符串存储对象,要求最后一层必须是特定的Language类型。最初的类型写法在调用getCurrLang(text['paragraph']['subText'])时触发类型错误:

Argument of type 'string | Text | Language' is not assignable to parameter of type 'Text | Language'. Type 'string' is not assignable to type 'Text | Language'

后续调整类型定义后仍未解决问题,求正确的类型定义方式或更优实现方案。


初始尝试代码

interface Text {
  [key: string | number]: ComponentText | Language;
}

interface Language {
  [key: string]: string;
}

const text: Text = {
  title: {
    en: "English text",
    sv: "Swedish text",
    fr: "French Text",
    es: "Spanish Text",
  },
  paragraph: {
    subText: {
      en: "English text",
      sv: "Swedish text",
      fr: "French Text",
      es: "Spanish Text",
    }
  }
}

// getCurrLang(s: any) 负责返回对应语言的字符串,若传入非Language类型则抛出错误

后续尝试代码

type LangCode = 'en' | 'sv' | 'fr' | 'es';

interface Text {
  [key: string]: ComponentText | Array<ComponentText>;
  language?: Language;
}

type Language = {
  [key in LangCode]: string;
};

正确的类型定义方案

核心思路是用递归类型区分嵌套节点和最终的语言节点,确保只有最底层是Language类型,上层嵌套节点只能包含其他嵌套节点或Language:

type LangCode = 'en' | 'sv' | 'fr' | 'es';

// 最终语言包类型:固定语言键值对
type Language = {
  [K in LangCode]: string;
};

// 递归类型:嵌套节点可以是其他NestedText或Language
type NestedText = {
  [key: string]: NestedText | Language;
};

// 使用示例
const text: NestedText = {
  title: {
    en: "English text",
    sv: "Swedish text",
    fr: "French Text",
    es: "Spanish Text",
  },
  paragraph: {
    subText: {
      en: "English text",
      sv: "Swedish text",
      fr: "French Text",
      es: "Spanish Text",
    },
    // 支持任意多层嵌套
    anotherSection: {
      subSubText: {
        en: "Deep nested English",
        sv: "Djupnästlad svenska"
      }
    }
  }
};

// 给getCurrLang添加类型约束,只接受Language类型参数
function getCurrLang(s: Language): string {
  // 示例:根据当前环境获取对应语言字符串
  const currLang: LangCode = 'en';
  return s[currLang];
}

// 此时调用不会报错,text.paragraph.subText被正确推断为Language类型
const subText = getCurrLang(text.paragraph.subText as Language);

额外优化建议

  1. 类型守卫:如果不想每次手动断言类型,可以添加类型守卫函数自动判断是否为Language类型:
function isLanguage(obj: NestedText | Language): obj is Language {
  return typeof obj === 'object' && !Array.isArray(obj) && ('en' in obj || 'sv' in obj || 'fr' in obj || 'es' in obj);
}

// 安全封装的语言获取函数
function safeGetCurrLang(s: NestedText | Language): string {
  if (isLanguage(s)) {
    return getCurrLang(s);
  }
  throw new Error("传入的不是有效的Language对象");
}

// 无需手动断言,自动推导类型
const safeSubText = safeGetCurrLang(text.paragraph.subText);
  1. 从类型层面杜绝错误:原问题的错误根源是初始类型允许节点直接存储string,递归类型NestedText只允许值为NestedText或Language,彻底避免了非法值的出现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 08:18:23