Recipe · Modules and packages

How do I import a JSON file in checked JavaScript?

  • Node.js (ES modules)
  • JSON modules
  • resolveJsonModule
  • Import attributes
  • Checked with TypeScript 5.9

The problem I import catalog.json into an ES module and want its fields checked. Which jsconfig.json settings does that need, and why does tsc ask for an import attribute?

The short answer

Import the file with a default import and the with { type: "json" } attribute. tsc types the value from the JSON itself:

prices.js prices.ts TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
import catalog from "./catalog.json" with { type: "json" };

/** @param {string} sku */
export function priceOf(sku) {
  return catalog.products.find((product) => product.sku === sku)?.cents;
}

console.log(catalog.currency.toLowerCase(), priceOf("LAMP-01"));
import catalog from "./catalog.json" with { type: "json" };

export function priceOf(sku: string) {
  return catalog.products.find((product) => product.sku === sku)?.cents;
}

console.log(catalog.currency.toLowerCase(), priceOf("LAMP-01"));
A JSON file imported with an import attribute.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

catalog.currency is a string, catalog.products an array of { sku, name, cents } objects, and a lookup returns number | undefined, all from the file.

Why it works

Node.js requires the type: "json" attribute when an ES module imports JSON: the importing code states what kind of module it expects, and a file that turns out to be something else is refused. TypeScript follows Node.js under module: "nodenext", reads the JSON file at check time, and infers its type the way it would infer an object literal. Under nodenext in TypeScript 5.9, JSON resolution (resolveJsonModule) is on by default.

The same thing in TypeScript

The TypeScript equivalent tab has the same import line; import attributes are JavaScript syntax, and TypeScript 5.3 and later checks them. Nothing in the JSON needs a type annotation in either language.

What goes wrong

Leave out the attribute and tsc reports what Node.js would refuse (TS1543); ask for a named export and it reports that JSON modules have only a default export (TS1544); a misspelled field is TS2551:

prices.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: prices.js(2,21): error TS1543: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'. prices.js(4,10): error TS1544: Named imports from a JSON file into an ECMAScript module are not allowed when 'module' is set to 'NodeNext'. prices.js(7,21): error TS2551: Property 'product' does not exist on type '{ currency: string; products: { sku: string; cents: number; }[]; }'. Did you mean 'products'? 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. prices.js(2,21): error TS1543: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'. prices.js(4,10): error TS1544: Named imports from a JSON file into an ECMAScript module are not allowed when 'module' is set to 'NodeNext'. prices.js(7,21): error TS2551: Property 'product' does not exist on type '{ currency: string; products: { sku: string; cents: number; }[]; }'. Did you mean 'products'? 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. prices.js(2,21): error TS1543: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'. prices.js(4,10): error TS1544: Named imports from a JSON file into an ECMAScript module are not allowed when 'module' is set to 'NodeNext'. prices.js(7,21): error TS2551: Property 'product' does not exist on type '{ currency: string; products: { sku: string; cents: number; }[]; }'. Did you mean 'products'? checked 21 October 2026
import catalog from "./catalog.json";error TS1543: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'.
import { currency } from "./catalog.json" with { type: "json" };error TS1544: Named imports from a JSON file into an ECMAScript module are not allowed when 'module' is set to 'NodeNext'.

console.log(catalog.product.length, currency);error TS2551: Property 'product' does not exist on type '{ currency: string; products: { sku: string; cents: number; }[]; }'. Did you mean 'products'?
No import attribute, a named import, and a misspelled field.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Gotchas

  • Values are widened. The type comes from the file’s content, so "USD" is typed string, not the literal "USD"; the JSON is data, and data can change.
  • No include entry is needed. tsc reads the JSON through the import; include lists the files that start the program, not every file it reaches.
  • assert is the old keyword. import catalog from "./catalog.json" assert { type: "json" } is TS2880 in TypeScript 5.9, which asks for with instead.

Questions

Do I need resolveJsonModule in jsconfig.json?

Not with module: "nodenext": TypeScript 5.9 turns it on by itself there (and under node20, preserve and bundler). With node16 or node18, or an older module setting, set "resolveJsonModule": true.

What about require("./catalog.json") in CommonJS?

It works without an import attribute, and tsc types the result from the file the same way, as long as JSON resolution is on.

Can I read the JSON with fs instead?

Yes, but then the result of JSON.parse is any; see typing JSON.parse. An import is checked against the actual file.

Sources

Last reviewed 21 October 2026.