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

如何将含字符串枚举属性的JSON转换为TypeScript接口类型

问题背景
  • 项目中使用 TypeScript 原生 string enum 实现后端到客户端的数据传输,受项目限制无法替换现有字符串枚举方案。
  • 编写 mock 模拟数据时,mock JSON 文件中包含对应的枚举字符串值。在 tsconfig 中开启 resolveJsonModule 配置后,通过 import testJson from './test.json' 方式导入 mock JSON 文件,TypeScript 会基于 JSON 的静态结构推导类型,此时枚举属性会被推导为普通 string 类型,而非对应的枚举类型。
  • 尝试将导入的 JSON 对象赋值为对应接口类型时,会抛出类型错误:Type 'string' is not assignable to type 'testEnum'.,如果使用 as any 类型断言绕过校验,会完全丢失类型安全性。

复现示例

const testJSON = {
  name: 'hello World',
  enumProp: "MYTESTENUM_2",
};

enum testEnum {
  MYTESTENUM = "MYTESTENUM",
  MYTESTENUM_2 = "MYTESTENUM_2",
}

interface testinterface {
  name: string;
  enumProp: testEnum;
}

// const castJsonFile: testinterface = testJSON; // 无法编译,字符串枚举类型无法完成转换
const castJsonFile: testinterface = testJSON as any;  // 通过any断言可通过编译,但丢失全部类型安全性


const appDiv: HTMLElement = document.getElementById('app');
appDiv.innerHTML = (castJsonFile.enumProp === testEnum.MYTESTENUM_2) as any as string;
解决方案

出现该问题的核心原因是 TypeScript 导入 JSON 模块时,默认会将字符串值推导为宽泛的 string 类型,不会自动匹配同值的字符串枚举。在不替换现有 string enum 方案的前提下,可根据场景选择以下三种实现方式:

方案1:as const 收窄类型(轻量化实现,无额外依赖)

导入 JSON 时增加 as const 断言,将字符串值收窄为精确字面量类型,再对枚举属性做定向类型断言,TypeScript 会自动校验值是否属于枚举范围,不会像 as any 一样跳过所有校验:

// 导入时加as const收窄类型
import testJson from './test.json' as const;

const castJsonFile: testinterface = {
  name: testJson.name,
  // 如果枚举值写错,TS会直接抛出类型错误,保留类型安全
  enumProp: testJson.enumProp as testEnum
};
  • 优点:零额外成本,代码改动量极小
  • 缺点:每个导入位置需要手动添加断言

方案2:通用类型守卫(双重校验,推荐生产环境使用)

编写通用字符串枚举校验工具,导入 JSON 后先经过校验逻辑转换,同时实现编译时类型匹配和运行时值校验,可拦截 mock 数据写错枚举值的问题:

/**
 * 通用字符串枚举值校验函数
 */
function isEnumValue<T extends string>(enumObj: Record<string, T>, value: unknown): value is T {
  return Object.values(enumObj).includes(value as T);
}

/**
 * 接口数据解析函数
 */
function parseTestInterface(raw: unknown): testinterface {
  if (typeof raw !== 'object' || raw === null) {
    throw new Error('mock数据格式错误');
  }
  const data = raw as Record<string, unknown>;
  if (typeof data.name !== 'string') {
    throw new Error('name字段类型错误');
  }
  if (!isEnumValue(testEnum, data.enumProp)) {
    throw new Error(`enumProp字段值错误,有效值为${Object.values(testEnum).join(',')}`);
  }
  return {
    name: data.name,
    enumProp: data.enumProp
  };
}

// 导入后解析,得到完全类型安全的对象
import testJson from './test.json';
const castJsonFile = parseTestInterface(testJson);
  • 优点:编译时+运行时双重校验,错误提示明确,完全保留类型安全性
  • 缺点:需要编写少量校验逻辑

方案3:JSON模块类型声明(固定结构mock适用,导入即得正确类型)

在 mock JSON 文件同目录下创建同名 .d.ts 类型声明文件,手动声明该JSON模块的对应类型,后续导入时 TypeScript 会自动识别为目标接口类型,无需额外断言:
以 test.json 为例,同目录下创建 test.json.d.ts:

declare module './test.json' {
  const value: testinterface;
  export default value;
}
  • 优点:业务代码导入时直接得到正确类型,无需额外写断言逻辑
  • 缺点:每个JSON文件需要维护对应类型声明,JSON结构变更时需要同步更新声明内容

注意:禁止直接对未收窄类型的字符串使用 as testEnum 断言,此时 TypeScript 不会校验值是否属于枚举范围,和 as any 一样存在类型安全隐患,例如 enumProp: 'invalid_value' as testEnum 不会触发TS报错。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 04:06:51