Importer un fichier json dans TypeScript

148

J'ai un JSONfichier qui ressemble à ceci:

{

  "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"
}

J'essaye de l'importer dans un .tsxfichier. Pour cela, j'ai ajouté ceci à la définition de type:

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

Et je l'importe comme ça.

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

Et dans le fichier, j'utilise la couleur primaryMaincomme colors.primaryMain. Cependant, j'obtiens une erreur:

La propriété 'primaryMain' n'existe pas sur le type 'typeof "* .json"

Sooraj
la source
3
Votre déclaration de module et votre formulaire d'importation ne sont pas d'accord.
Aluan Haddad
2
Ça vous dérange de montrer un exemple? Je suis noob dactylographié.
Sooraj
Copie

Réponses:

93

Le formulaire d'importation et la déclaration du module doivent convenir de la forme du module, de ce qu'il exporte.

Lorsque vous écrivez (une pratique sous-optimale pour l'importation de JSON depuis TypeScript 2.9 lors du ciblage de formats de modules compatibles voir note )

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

Vous déclarez que tous les modules dont le spécificateur se termine par .jsonont une seule exportation nommée default .

Il existe plusieurs façons de consommer correctement un tel module, notamment

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

et

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

et

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

et

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

La première forme est la meilleure et le sucre syntaxique qu'elle exploite est la raison même pour laquelle JavaScript defaultexporte.

Cependant, j'ai mentionné les autres formulaires pour vous donner une idée de ce qui ne va pas. Portez une attention particulière au dernier. requirevous donne un objet représentant le module lui-même et non ses liaisons exportées.

Alors pourquoi l'erreur? Parce que tu as écrit

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

Et pourtant, il n'y a pas d'export nommé primaryMaindéclaré par votre "*.json".

Tout cela suppose que votre chargeur de module fournit le JSON comme defaultexportation comme suggéré par votre déclaration d'origine.

Remarque: depuis TypeScript 2.9, vous pouvez utiliser l' --resolveJsonModuleindicateur du compilateur pour que TypeScript analyse les .jsonfichiers importés et fournisse des informations correctes concernant leur forme, évitant ainsi le besoin d'une déclaration de module générique et validant la présence du fichier. Ceci n'est pas pris en charge pour certains formats de module cible.

Aluan Haddad
la source
1
@Royi qui dépend de votre chargeur. Pour les fichiers distants, envisagez d'utiliserawait import('remotepath');
Aluan Haddad
28
Continuez à faire défiler, réponse plus à jour ci-dessous.
jbmusso
@jbmusso J'ai ajouté quelques informations concernant les améliorations apportées par les versions ultérieures de TypeScript mais je ne pense pas que cette réponse soit obsolète car elle est conceptuelle. Cependant, je suis ouvert aux suggestions d'améliorations supplémentaires.
Aluan Haddad
1
Le risque est que certaines personnes puissent simplement copier / coller les premières lignes de votre réponse, ne corrigeant que le symptôme et non la cause profonde. Je crois que la réponse de @ kentor donne plus de détails et fournit une réponse plus complète. Une recommandation serait de déplacer votre note au-dessus de votre réponse, en indiquant clairement que c'est la bonne façon d'aborder ce problème dès aujourd'hui.
jbmusso
@Atombit cela a évidemment fonctionné pour de nombreuses personnes, dont moi. Voulez-vous expliquer ce qui ne fonctionne pas avant de voter contre la réponse acceptée?
Aluan Haddad
270

Avec TypeScript 2.9. +, Vous pouvez simplement importer des fichiers JSON avec typesafety et intellisense comme ceci:

import colorsJson from '../colors.json'; // This import style requires "esModuleInterop", see "side notes"
console.log(colorsJson.primaryBright);

Assurez-vous d'ajouter ces paramètres dans la compilerOptionssection de votre tsconfig.json( documentation ):

"resolveJsonModule": true,
"esModuleInterop": true,

Notes secondaires:

  • Typescript 2.9.0 a un bogue avec cette fonctionnalité JSON, il a été corrigé avec 2.9.2
  • L'esModuleInterop n'est nécessaire que pour l'importation par défaut de colorsJson. Si vous laissez la valeur false, vous devez l'importer avecimport * as colorsJson from '../colors.json'
Kentor
la source
18
Vous n'en avez pas nécessairement besoin esModuleInterop, mais vous devez le faire import * as foo from './foo.json';- cela esModuleInteropme causait d'autres problèmes lorsque j'ai essayé de l'activer.
mpen le
1
Vous avez raison, j'aurais dû ajouter cela en remarque :-).
kentor
10
Remarque: l'option "resolverJsonModule" ne peut pas être spécifiée sans la stratégie de résolution du module "node", vous devez donc également la mettre "moduleResolution": "node"dans votre tsconfig.json. Cela présente également l'inconvénient que les *.jsonfichiers que vous souhaitez importer doivent se trouver à l'intérieur "rootDir". Source: blogs.msdn.microsoft.com/typescript/2018/05/31/…
Benny Neugebauer
2
@mpen c'est correct mais import * as foo from './foo.json'c'est le mauvais formulaire d'importation. Il devrait l'être import foo = require('./foo.json');lorsque vous ne l'utilisez pasesModuleInterop
Aluan Haddad
1
La seule partie dont j'avais besoin était "resolveJsonModule": trueet tout va bien
Michael Elliott
10

Il est facile d'utiliser la version 2.9+ dactylographiée. Ainsi, vous pouvez facilement importer des fichiers JSON comme @kentor le décrit .

Mais si vous devez utiliser des versions plus anciennes:

Vous pouvez accéder aux fichiers JSON de manière plus TypeScript. Tout d'abord, assurez-vous que votre nouvel typings.d.tsemplacement est le même que celui de la includepropriété dans votre tsconfig.jsonfichier.

Si vous n'avez pas de propriété include dans votre tsconfig.jsonfichier. Ensuite, la structure de votre dossier devrait être comme ça:

- app.ts
+ node_modules/
- package.json
- tsconfig.json
- typings.d.ts

Mais si vous avez une includepropriété dans votre tsconfig.json:

{
    "compilerOptions": {
    },
    "exclude"        : [
        "node_modules",
        "**/*spec.ts"
    ], "include"        : [
        "src/**/*"
    ]
}

Ensuite, vous typings.d.tsdevriez être dans le srcrépertoire comme décrit dans la includepropriété

+ node_modules/
- package.json
- tsconfig.json
- src/
    - app.ts
    - typings.d.ts

Comme dans la plupart des réponses, vous pouvez définir une déclaration globale pour tous vos fichiers JSON.

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

mais je préfère une version plus typée de ceci. Par exemple, disons que vous avez un fichier de configuration config.jsoncomme celui-ci:

{
    "address": "127.0.0.1",
    "port"   : 8080
}

Ensuite, nous pouvons déclarer un type spécifique pour cela:

declare module 'config.json' {
    export const address: string;
    export const port: number;
}

Il est facile d'importer dans vos fichiers dactylographiés:

import * as Config from 'config.json';

export class SomeClass {
    public someMethod: void {
        console.log(Config.address);
        console.log(Config.port);
    }
}

Mais en phase de compilation, vous devez copier manuellement les fichiers JSON dans votre dossier dist. Je viens d'ajouter une propriété de script à ma package.jsonconfiguration:

{
    "name"   : "some project",
    "scripts": {
        "build": "rm -rf dist && tsc && cp src/config.json dist/"
    }
}
Fırat KÜÇÜK
la source
Est-ce que rm -rf est une chose Linux / Unix, ou cela fonctionnera-t-il aussi sur le vieux Windurz?
Cody
merci, mes typings.d.ts n'étaient pas à leur place. Dès que je suis passé à / src, le message d'erreur a disparu.
Gi1ber7
1
@Cody Ce n'est en effet qu'une chose Linux / Unix.
Maxie Berkmann le
7

Dans votre fichier de définition TS, par exemple typings.d.ts`, vous pouvez ajouter cette ligne:

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

Ajoutez ensuite ceci dans votre fichier dactylographié (.ts): -

import * as data from './colors.json';
const word = (<any>data).name;
Mehadi Hassan
la source
2
C'est une très mauvaise idée.
Aluan Haddad
3
cela vous dérangerait-il d'expliquer pourquoi c'est mauvais ??? Je ne suis pas expert en tapuscrit. @AluanHaddad
Mehadi Hassan
5
Votre assertion de type anydit deux choses. 1) que vous avez déclaré ou importé incorrectement à première vue simplement par définition. Vous ne devriez en aucun cas avoir besoin de placer une assertion de type sur une importation d'un module que vous avez vous-même déclaré. 2) même si vous avez un chargeur insensé qui fonctionne d'une manière ou d'une autre à l'exécution, Dieu vous en garde, ce serait toujours un moyen incroyablement déroutant et le plus fragile d'accéder à un module de la forme donnée. * as x fromet x fromsont encore plus incompatibles en termes d'exécution que ce qui est dans l'OP. Sérieusement, ne faites pas ça.
Aluan Haddad
5
Merci pour votre réponse. J'ai bien compris. @AluanHaddad
Mehadi Hassan
2

Une autre façon d'aller

const data: {[key: string]: any} = require('./data.json');

C'est ainsi que vous pouvez toujours définir le type json que vous voulez et ne pas avoir à utiliser de joker.

Par exemple, le type personnalisé json.

interface User {
  firstName: string;
  lastName: string;
  birthday: Date;
}
const user: User = require('./user.json');
Monsieur Br
la source
2
Cela n'a rien à voir avec la question et constitue également une mauvaise pratique.
Aluan Haddad
0

Souvent, dans les applications Node.js, un .json est nécessaire. Avec TypeScript 2.9, --resolveJsonModule permet d'importer, d'extraire des types et de générer des fichiers .json.

Exemple #

// tsconfig.json

{
    "compilerOptions": {
        "module": "commonjs",
        "resolveJsonModule": true,
        "esModuleInterop": true
    }
}

// .ts

import settings from "./settings.json";

settings.debug === true;  // OK
settings.dry === 2;  // Error: Operator '===' cannot be applied boolean and number


// settings.json

{
    "repo": "TypeScript",
    "dry": false,
    "debug": false
}
par: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html

Ruben Palavecino
la source