Importazione di file json in TypeScript


148

Ho un JSONfile simile al seguente:

{

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

Sto cercando di importarlo in un .tsxfile. Per questo ho aggiunto questo alla definizione del tipo:

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

E lo sto importando in questo modo.

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

E nel file, uso il colore primaryMaincome colors.primaryMain. Tuttavia ricevo un errore:

La proprietà 'primaryMain' non esiste sul tipo 'typeof "* .json"


3
La dichiarazione del modulo e il modulo di importazione non sono d'accordo.
Aluan Haddad,

2
Ti dispiace mostrare un esempio? Sono dattiloscritto noob.
Sooraj,

Risposte:


93

Il modulo di importazione e la dichiarazione del modulo devono concordare sulla forma del modulo, su ciò che esporta.

Quando si scrive (una pratica non ottimale per l'importazione di JSON da TypeScript 2.9 durante il targeting di formati di moduli compatibili, vedere la nota )

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

Stai affermando che tutti i moduli che hanno un identificatore che termina .jsonhanno una singola esportazione denominata default .

Esistono diversi modi in cui è possibile utilizzare correttamente un tale modulo, incluso

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

e

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

e

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

e

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

La prima forma è la migliore e lo zucchero sintattico che fa leva è la vera ragione per cui JavaScript ha defaultesportato.

Tuttavia, ho citato le altre forme per darti un suggerimento su cosa non va. Presta particolare attenzione all'ultimo. requireti dà un oggetto che rappresenta il modulo stesso e non i suoi collegamenti esportati.

Quindi perché l'errore? Perché hai scritto

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

Eppure non esiste esportazione primaryMaindichiarata dal tuo "*.json".

Tutto ciò presuppone che il caricatore del modulo fornisca JSON come defaultesportazione, come suggerito dalla dichiarazione originale.

Nota: a partire da TypeScript 2.9, è possibile utilizzare il --resolveJsonModuleflag del compilatore per fare in modo che TypeScript analizzi i .jsonfile importati e fornisca informazioni corrette sulla loro forma, evitando la necessità di una dichiarazione del modulo jolly e convalidando la presenza del file. Questo non è supportato per alcuni formati del modulo di destinazione.


1
@Royi che dipende dal tuo caricatore. Per i file remoti prendere in considerazione l'utilizzo diawait import('remotepath');
Aluan Haddad,

28
Continua a scorrere, risposta più aggiornata di seguito.
jbmusso,

@jbmusso Ho aggiunto alcune informazioni sui miglioramenti introdotti dalle versioni successive di TypeScript ma non credo che questa risposta sia obsoleta perché concettuale. Tuttavia, sono aperto a suggerimenti per ulteriori miglioramenti.
Aluan Haddad,

1
Il rischio è che alcune persone possano semplicemente copiare / incollare le prime righe della risposta, risolvendo solo il sintomo e non la causa principale. Credo che la risposta di @ kentor fornisca maggiori dettagli e fornisca una risposta più completa. Una raccomandazione sarebbe quella di spostare la nota in cima alla risposta, affermando chiaramente che questo è il modo corretto per affrontare questo problema ad oggi.
jbmusso,

@Atombit ha ovviamente funzionato per molte persone, incluso me. Ti interessa spiegare cosa non funziona prima di sottoporre a votazione negativa la risposta accettata?
Aluan Haddad,

270

Con TypeScript 2.9. + Puoi semplicemente importare file JSON con typesafety e intellisense in questo modo:

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

Assicurati di aggiungere queste impostazioni nella compilerOptionssezione della tsconfig.json( documentazione ):

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

Note a margine:

  • Typescript 2.9.0 ha un bug con questa funzione JSON, è stato corretto con 2.9.2
  • EsModuleInterop è necessario solo per l'importazione predefinita di colorsJson. Se lo lasci impostato su false, devi importarlo conimport * as colorsJson from '../colors.json'

18
Non è necessariamente necessario esModuleInterop, ma poi devi farlo import * as foo from './foo.json';: questo mi ha esModuleInteropcausato altri problemi quando ho provato ad abilitarlo.
Aprire il

1
Hai ragione, avrei dovuto aggiungerlo come nota a margine :-).
Kent,

10
Nota: L'opzione "resolJsonModule" non può essere specificata senza la strategia di risoluzione del modulo "nodo", quindi è necessario inserirla anche "moduleResolution": "node"in tsconfig.json. Inoltre, il lato negativo è che i *.jsonfile che si desidera importare devono trovarsi all'interno "rootDir". Fonte: blogs.msdn.microsoft.com/typescript/2018/05/31/…
Benny Neugebauer

2
@mpen è corretto ma import * as foo from './foo.json'è il modulo di importazione errato. Dovrebbe essere import foo = require('./foo.json');quando non si utilizzaesModuleInterop
Aluan Haddad

1
L'unica parte di cui avevo bisogno era "resolveJsonModule": truee tutto va bene
Michael Elliott,

10

È facile usare la versione dattiloscritta 2.9+. Quindi puoi importare facilmente i file JSON come descritto da @kentor .

Ma se è necessario utilizzare versioni precedenti:

Puoi accedere ai file JSON in un modo più TypeScript. Innanzitutto, assicurati che la nuova typings.d.tsposizione sia la stessa della includeproprietà nel tuo tsconfig.jsonfile.

Se non hai una proprietà include nel tuo tsconfig.jsonfile. Quindi la struttura delle cartelle dovrebbe essere così:

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

Ma se hai una includeproprietà nel tuo tsconfig.json:

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

Quindi typings.d.tsdovresti essere nella srcdirectory come descritto nella includeproprietà

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

Come in molte delle risposte, è possibile definire una dichiarazione globale per tutti i file JSON.

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

ma preferisco una versione più tipizzata di questo. Ad esempio, supponiamo che tu abbia un file di configurazione del config.jsongenere:

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

Quindi possiamo dichiarare un tipo specifico per esso:

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

È facile importare nei tuoi file dattiloscritto:

import * as Config from 'config.json';

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

Ma in fase di compilazione, dovresti copiare manualmente i file JSON nella tua cartella dist. Aggiungo solo una proprietà di script alla mia package.jsonconfigurazione:

{
    "name"   : "some project",
    "scripts": {
        "build": "rm -rf dist && tsc && cp src/config.json dist/"
    }
}

Rm -rf è una cosa Linux / Unix, o funzionerà anche su Windurz?
Cody,

grazie, il mio typings.d.ts era fuori posto. Non appena sono passato a / src il messaggio di errore è scomparso.
Gi1ber7

1
@Cody È davvero solo una cosa Linux / Unix.
Maxie Berkmann,

7

Nel tuo file di definizione TS, ad esempio typings.d.ts`, puoi aggiungere questa riga:

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

Quindi aggiungi questo nel tuo file dattiloscritto (.ts): -

import * as data from './colors.json';
const word = (<any>data).name;

2
Questa è una pessima idea.
Aluan Haddad,

3
ti dispiacerebbe spiegare perché è male ??? Non sono esperto di dattiloscritto. @AluanHaddad
Mehadi Hassan

5
La tua affermazione tipo di anydice due cose. 1) che hai dichiarato o importato in modo errato sulla faccia semplicemente per definizione. Non dovresti mai in nessun caso mettere un'asserzione di tipo sull'importazione di un modulo che hai dichiarato tu stesso. 2) anche se hai un caricatore folle che in qualche modo risolve questo problema in fase di esecuzione, gli dei lo vietano, sarebbe comunque un modo follemente confuso e il più fragile di accedere a un modulo della forma data. * as x frome x fromsono ancora più ineguagliati in termini di runtime rispetto a quanto è presente nel PO. Seriamente non farlo.
Aluan Haddad,

5
Grazie per la vostra risposta. Ho capito chiaramente. @AluanHaddad
Mehadi Hassan,

2

Un altro modo di andare

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

In questo modo è ancora possibile definire il tipo json che si desidera e non è necessario utilizzare il carattere jolly.

Ad esempio, digitare personalizzato json.

interface User {
  firstName: string;
  lastName: string;
  birthday: Date;
}
const user: User = require('./user.json');

2
Questo non ha nulla a che fare con la domanda ed è anche una cattiva pratica.
Aluan Haddad,

0

Spesso nelle applicazioni Node.js è necessario un .json. Con TypeScript 2.9, --resolveJsonModule consente di importare, estrarre tipi e generare file .json.

Esempio #

// 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
}
di: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.