TypeScript导入JSON时模块包裹行为不一致的问题如何解决?
行为不一致的原因
这个问题的本质是TypeScript类型推断和实际运行时的模块结构不匹配,由两个规则共同导致:
- 你用的构建工具(比如Webpack、旧版Vite)处理JSON文件时,默认会生成两种导出:JSON每个顶级字段的单独命名导出、完整JSON对象的
default导出。你用import * as a语法得到的是完整的模块命名空间对象,不是JSON本身,所以访问a.one刚好能命中命名导出的字段拿到值,但模块对象本身还带了default、__esModule等内置属性,直接赋值的话自然就会拿到完整的Module实例。 - 当你开启了TS的
resolveJsonModule配置后,TS会把import * as导入的JSON模块的类型直接推断为JSON本身的结构,既允许你直接访问字段,也允许你把导入值直接当做JSON对象赋值,不会提示你这是模块命名空间对象,就造成了“内部访问属性正常、直接赋值异常”的不一致现象。
无需工具函数的解决方案
方案1:改用默认导入(最推荐)
直接把导入语句改成默认导入,拿到的直接是完整的JSON对象,所有用法都符合预期:
// 修改导入语句 import a from "./a.json"; import b from "./b.json"; // 后续代码不需要调整 Things.one = a.one; Things.two = a.two; Things.three = b;
只需要在tsconfig.json中开启allowSyntheticDefaultImports配置即可,如果已经开启了esModuleInterop,这个配置会自动生效。
方案2:赋值时显式取default
如果不想修改TS配置,只需要在赋值完整JSON对象的时候主动取模块的default属性即可:
Things.three = b.default;
方案3:关闭构建工具的JSON命名导出
如果用的是Webpack,可以修改json-loader的配置关闭namedExports,强制JSON只能通过default导出,从根源上避免两种用法的行为差异:
// webpack.config.js 对应规则配置 module: { rules: [ { test: /\.json$/, loader: 'json-loader', options: { namedExports: false } } ] }
方案4:开启esModuleInterop
直接在tsconfig.json中设置esModuleInterop: true,TS会自动处理JSON、CommonJS模块的导入兼容,默认导入的类型和运行时行为会完全对齐,不需要额外处理。
内容的提问来源于stack exchange,提问作者user16985970
相关产品推荐
相关产品推荐

