Search code examples
jsontypescript

Importing JSON file in TypeScript


I have a JSON file that looks like following:

{

  "primaryBright":    "#2DC6FB",
  "primaryMain":      "#05B4F0",
  "primaryDarker":    "#04A1D7",
  "primaryDarkest":   "#048FBE",

  "secondaryBright":  "#4CD2C0",
  "secondaryMain":    "#00BFA5",
  "secondaryDarker":  "#009884",
  "secondaryDarkest": "#007F6E",

  "tertiaryMain":     "#FA555A",
  "tertiaryDarker":   "#F93C42",
  "tertiaryDarkest":  "#F9232A",

  "darkGrey":         "#333333",
  "lightGrey":        "#777777"
}

I'm trying to import it into a .tsx file. For this I added this to the type definition:

declare module "*.json" {
  const value: any;
  export default value;
}

And I'm importing it like this.

import colors = require('../colors.json')

And in the file, I use the color primaryMain as colors.primaryMain. However I get an error:

Property 'primaryMain' does not exist on type 'typeof "*.json"


Solution

  • The import form and the module declaration need to agree about the shape of the module, about what it exports.

    When you write (a suboptimal practice for importing JSON since TypeScript 2.9 when targeting compatible module formatssee note)

    declare module "*.json" {
      const value: any;
      export default value;
    }
    

    You are stating that all modules that have a specifier ending in .json have a single export named default.

    There are several ways you can correctly consume such a module including

    import a from "a.json";
    a.primaryMain
    

    and

    import * as a from "a.json";
    a.default.primaryMain
    

    and

    import {default as a} from "a.json";
    a.primaryMain
    

    and

    import a = require("a.json");
    a.default.primaryMain
    

    The first form is the best and the syntactic sugar it leverages is the very reason JavaScript has default exports.

    However I mentioned the other forms to give you a hint about what's going wrong. Pay special attention to the last one. require gives you an object representing the module itself and not its exported bindings.

    So why the error? Because you wrote

    import a = require("a.json");
    a.primaryMain
    

    And yet there is no export named primaryMain declared by your "*.json".

    All of this assumes that your module loader is providing the JSON as the default export as suggested by your original declaration.

    Note: Since TypeScript 2.9, you can use the --resolveJsonModule compiler flag to have TypeScript analyze imported .json files and provide correct information regarding their shape obviating the need for a wildcard module declaration and validating the presence of the file. This is not supported for certain target module formats.