Importing JSON file in TypeScript
Solution 1
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.
Solution 2
With TypeScript 2.9.+ you can simply import JSON files with benefits like typesafety and intellisense by doing this:
import colorsJson from '../colors.json'; // This import style requires "esModuleInterop", see "side notes"
console.log(colorsJson.primaryBright);
Make sure to add these settings in the compilerOptions
section of your tsconfig.json
(documentation):
"resolveJsonModule": true,
"esModuleInterop": true,
Side notes:
- Typescript 2.9.0 has a bug with this JSON feature, it was fixed with 2.9.2
- The esModuleInterop is only necessary for the default import of the colorsJson. If you leave it set to false then you have to import it with
import * as colorsJson from '../colors.json'
Solution 3
It's easy to use typescript version 2.9+. So you can easily import JSON files as @kentor decribed.
But if you need to use older versions:
You can access JSON files in more TypeScript way. First, make sure your new typings.d.ts
location is the same as with the include
property in your tsconfig.json
file.
If you don't have an include property in your tsconfig.json
file. Then your folder structure should be like that:
- app.ts
+ node_modules/
- package.json
- tsconfig.json
- typings.d.ts
But if you have an include
property in your tsconfig.json
:
{
"compilerOptions": {
},
"exclude" : [
"node_modules",
"**/*spec.ts"
], "include" : [
"src/**/*"
]
}
Then your typings.d.ts
should be in the src
directory as described in include
property
+ node_modules/
- package.json
- tsconfig.json
- src/
- app.ts
- typings.d.ts
As In many of the response, You can define a global declaration for all your JSON files.
declare module '*.json' {
const value: any;
export default value;
}
but I prefer a more typed version of this. For instance, let's say you have configuration file config.json
like that:
{
"address": "127.0.0.1",
"port" : 8080
}
Then we can declare a specific type for it:
declare module 'config.json' {
export const address: string;
export const port: number;
}
It's easy to import in your typescript files:
import * as Config from 'config.json';
export class SomeClass {
public someMethod: void {
console.log(Config.address);
console.log(Config.port);
}
}
But in compilation phase, you should copy JSON files to your dist folder manually. I just add a script property to my package.json
configuration:
{
"name" : "some project",
"scripts": {
"build": "rm -rf dist && tsc && cp src/config.json dist/"
}
}
Solution 4
Here's how to import a json file at runtime
import fs from 'fs'
var dataArray = JSON.parse(fs.readFileSync('data.json', 'utf-8'))
This way you avoid issues with tsc slowing down or running out of memory when importing large files, which can happen when using resolveJsonModule.
Solution 5
You should add
"resolveJsonModule": true
as part of compilerOptions to tsconfig.json.
Related videos on Youtube
Comments
-
Sooraj over 2 years
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
ascolors.primaryMain
. However I get an error:Property 'primaryMain' does not exist on type 'typeof "*.json"
-
Aluan Haddad about 6 yearsYour module declaration and your import form disagree.
-
Sooraj about 6 yearsDo you mind showing an example? I'm typescript noob.
-
chharvey almost 6 yearsPossible duplicate of Typescript compiler error when importing json file
-
-
mpen almost 6 yearsYou don't necessarily need
esModuleInterop
, but then you have to doimport * as foo from './foo.json';
-- theesModuleInterop
was causing other problems for me when I tried enabling it. -
kentor almost 6 yearsYou are right, I should have added that as a side note :-).
-
Aluan Haddad almost 6 years@Royi that depends on your loader. For remote files consider using
await import('remotepath');
-
Benny Neugebauer over 5 yearsNote: Option "resolveJsonModule" cannot be specified without "node" module resolution strategy, so you also need to put
"moduleResolution": "node"
into yourtsconfig.json
. It also comes with the downside, that the*.json
files you want to import need to be inside of"rootDir"
. Source: blogs.msdn.microsoft.com/typescript/2018/05/31/… -
Cody over 5 yearsIs rm -rf a Linux/Unix thing, or will that work on ol' Windurz also?
-
Aluan Haddad over 5 years@mpen that's correct but
import * as foo from './foo.json'
is the wrong import form. It should beimport foo = require('./foo.json');
when not usingesModuleInterop
-
Aluan Haddad about 5 yearsThis has nothing to do with the question and is also bad practice.
-
Michael Elliott about 5 yearsOnly part i needed was
"resolveJsonModule": true
and all is well -
jbmusso about 5 yearsKeep scrolling, more up-to-date answer below.
-
Aluan Haddad about 5 years@jbmusso I added some information regarding the improvements introduced by later versions of TypeScript but I don't think this answer is out of date because it is conceptual. However, I'm open to suggestions for further improvements.
-
jbmusso about 5 yearsThe risk is that some people could simply copy/paste the first lines of your answer, only fixing the symptom and not the root cause. I believe @kentor's answer yields greater details and provides a more complete answer. A recommendation would be to move your Note on top of your answer, clearly stating that this is the correct way to tackle this issue as of today.
-
Gi1ber7 over 4 yearsthank you, my typings.d.ts was out of place. As soon as I moved to /src the error message disappeared.
-
Maxie Berkmann over 4 years@Cody It's indeed only a Linux/Unix thing.
-
Aluan Haddad over 4 years@Atombit it has obviously worked for many people, including me. Care to explain what's not working before downvoting the accepted answer?
-
Ε Г И І И О over 3 yearsI did this but I'm getting the dates as strings. What should I do to get the proper date objects deserialized from json?
-
Volmarg Reiso over 3 yearsThese are proper
compilerOptions
- work like a charm -
Craig Hicks over 3 years@BennyNeugebauer - You wrote: "It also comes with the downside, that the *.json files you want to import need to be inside of "rootDir"" --- If that was in your link when you wrote the comment, it is gone now. Instead the comment shows an example where the json file is not in the root directory.
-
Zar Shardan about 3 yearsI also needed "allowSyntheticDefaultImports": true for TS 2.9.2
-
forresthopkinsa about 3 years
import * as foo from './bar.json'
worked for me. TS 3.8 -
Cyclion over 2 years
import {default as yourPreferredName} from "any.json";
works like a charm -
Aluan Haddad over 2 years@Cyclion
import {default as yourPreferredName} from "any.json";
is proper but it's usually preferable to writeimport yourPreferredName from "any.json";
which has precisely the same meaning. -
J-D3V about 2 yearsYea but if its such a large file that it slows the compiler out of memory, you just called FS.readFileSync on that huge file, then you parsed it, synchronously. Don't you think it would be better to load it asynchronously? Given each situation is different, but as a generic answer to an issue I just don't see much benefit here.
-
Bruno Degomme about 2 yearsGood point, but for some reason I had tsc crashing even for relatively small json files (< 1Mb, 20k lines), and reading&parsing the same file synchronously at runtime was not an issue. Not sure why that would be the case, I think tsc just doesn't handle compiling large arrays very well.