Importing JSON with custom types in TypeScript

May 22nd 2026 TypeScript

When you want to embed some static data in your TypeScript application, it's very convenient to store it as a JSON file and import it directly in a source code file. The compiler even implies static typing for the data. If it doesn't describe your data good enough, there's a way to provide your own type definition.

To make imports work with JSON data, you need to enable resolveJsonModule in your tsconfig.json file (it seems to be enabled by default when you have module compiler option set to node20 or nodenext, but it doesn't hurt to set it explicitly anyway):

{
  "resolveJsonModule": true
}

Then you can import a JSON file just like a regular TypeScript file:

import lookups from "./lookups.json";

This initializes a lookups variable with the data from the JSON file and you can use it in your code like any other variable:

for (const lookup of lookups) {
  // ...
}

And it's not even typed as any. The compiler assigns it a type which matches the file contents.

In many cases this works good enough. But it made the types too specific for my use when I was dealing with a key value dictionary:

[
  {
    "separator": ",",
    "index": 2,
    "lookups": {
      "one": "1",
      "two": "2",
      "three": "3",
      "four": "4",
      "five": "5"
    }
  },
  {
    "separator": ";",
    "index": 1,
    "lookups": {
      "1": "a",
      "2": "b",
      "3": "c",
      "4": "d",
      "5": "e"
    }
  }
]

When I tried to access the lookups dictionary field by a string key, I got a compiler error:

Type error when accessing imported JSON data

When I took a look at the type the compiler prepared for me, it quickly became obvious why it didn't work:

const lookup:
  | {
      separator: string;
      index: number;
      lookups: {
        one: string;
        two: string;
        three: string;
        four: string;
        five: string;
        "1"?: never;
        "2"?: never;
        "3"?: never;
        "4"?: never;
        "5"?: never;
      };
    }
  | {
      separator: string;
      index: number;
      lookups: {
        "1": string;
        "2": string;
        "3": string;
        "4": string;
        "5": string;
        one?: never;
        two?: never;
        three?: never;
        four?: never;
        five?: never;
      };
    };

The type was much too specific for my needs. I wanted the lookups field to be treated as a Record<string, string>.

Fortunately, there's a way to specify your own type definitions for data from an imported JSON file. To make it work, you need to set the allowArbitraryExtensions compiler option instead of resolveJsonModule:

{
  "allowArbitraryExtensions": true
}

Then you can create a type definition for your JSON file with the same base filename, but with a .d.json.ts extension instead of .json, i.e., for my lookups.json file I had to create a type definition file lookups.d.json.ts with the following contents:

declare const lookups: {
  separator: string;
  index: number;
  lookups: Record<string, string>;
}[];

export default lookups;

With these two changes, the compiler imported the JSON file with my types instead of its own implied ones. And with that, the compiler error for my dictionary lookup by key was resolved.

You can find a sample project in my GitHub repository. The last commit showcases the approach with the allowArbitraryExtensions compiler option and the one before that uses the resolveJsonModule compiler option.

Although resolveJsonModule works great for importing JSON files in most cases, it's good to know that there is an option to override default implied type information when you need to by using allowArbitraryExtensions. Just keep in mind that it's your responsibility to make sure that the data in the JSON file matches your type definition. Otherwise you can expect run-time errors in your application when accessing that data.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License