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

如何优雅且类型安全地实现多语言数据转换(TS+RN+MongoDB)

优雅实现类型安全的多语言对象转换

核心思路

你的方向是对的,问题出在缺少类型层面的递归映射——没有告诉TypeScript转换后的对象会把所有MultilingualString字段替换为string。通过定义一个泛型类型Localized<T>,可以递归地完成类型转换,同时配合转换函数确保运行时逻辑和类型一致。

步骤1:定义递归泛型类型Localized<T>

这个类型会遍历对象的所有字段,把MultilingualString类型替换为string,同时处理嵌套对象的情况:

type MultilingualString = {
  en: string;
  he: string;
};

type Localized<T> = T extends MultilingualString
  ? string
  : T extends object
    ? { [K in keyof T]: Localized<T[K]> }
    : T;

步骤2:修改转换函数,返回Localized<T>类型

重构determineObjectLanguage函数,返回新对象(避免修改原对象带来的副作用),并指定返回类型为Localized<T>:

export const isMultilingualString = (value: unknown): value is MultilingualString => {
  return (
    typeof value === 'object' &&
    value !== null &&
    'en' in value &&
    typeof (value as MultilingualString).en === 'string' &&
    'he' in value &&
    typeof (value as MultilingualString).he === 'string'
  );
};

function determineObjectLanguage<T>(object: T): Localized<T> {
  // 实际从i18n-next获取当前语言,比如:i18n.language
  const currentLanguageCode: keyof MultilingualString = "en";

  const result: Partial<Localized<T>> = {};

  for (const key of Object.keys(object) as Array<keyof T>) {
    const value = object[key];

    if (isMultilingualString(value)) {
      // 替换为当前语言的字符串
      result[key] = value[currentLanguageCode] as Localized<T>[typeof key];
    } else if (typeof value === 'object' && value !== null) {
      // 递归处理嵌套对象
      result[key] = determineObjectLanguage(value) as Localized<T>[typeof key];
    } else {
      // 非对象/多语言字段直接保留
      result[key] = value as Localized<T>[typeof key];
    }
  }

  return result as Localized<T>;
}

步骤3:类型安全的使用示例

定义数据库层的类型,转换后自动得到本地化后的类型,无需联合类型:

// 数据库存储的对象类型
interface DBItem {
  name: MultilingualString;
  description: MultilingualString;
  price: number;
  category: {
    title: MultilingualString;
    id: string;
  };
}

// 模拟从数据库获取的数据
const dbItem: DBItem = {
  name: { en: "Apple", he: "תפוח" },
  description: { en: "Fresh organic apple", he: "תפוח אורגני טרי" },
  price: 2.99,
  category: {
    title: { en: "Fruits", he: "פירות" },
    id: "cat-fruit"
  }
};

// 转换后得到Localized<DBItem>类型,所有多语言字段都是string
const localizedItem = determineObjectLanguage(dbItem);

// 类型安全:localizedItem.name 是 string,无需类型断言
console.log(localizedItem.name); // "Apple"
console.log(localizedItem.category.title); // "Fruits"

额外优化建议

  1. 异步处理:如果当前语言需要异步获取(比如从存储读取),可以把函数改成async,返回Promise<Localized<T>>,逻辑保持一致。
  2. 数组支持:如果需要处理数组,可以在函数中添加数组判断逻辑,遍历数组元素进行转换:
    else if (Array.isArray(value)) {
      result[key] = value.map(item => determineObjectLanguage(item)) as Localized<T>[typeof key];
    }
    
  3. 不可变数据:返回新对象而非修改原对象,更符合React-Native的状态管理原则,避免意外的副作用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 13:28:09