Importar archivo json en TypeScript

146

Tengo un JSONarchivo que se parece a lo siguiente:

{

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

Estoy tratando de importarlo a un .tsxarchivo. Para esto agregué esto a la definición de tipo:

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

Y lo estoy importando así.

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

Y en el archivo, uso el color primaryMaincomo colors.primaryMain. Sin embargo me sale un error:

La propiedad 'primaryMain' no existe en el tipo 'typeof "* .json"

Sooraj
fuente
3
La declaración de su módulo y su formulario de importación no están de acuerdo.
Aluan Haddad
2
¿Te importa mostrar un ejemplo? Soy novato mecanografiado.
Sooraj
Posible duplicado del error del compilador de
mecanografiado

Respuestas:

93

El formulario de importación y la declaración del módulo deben estar de acuerdo sobre la forma del módulo, sobre lo que exporta.

Cuando escribe (una práctica subóptima para importar JSON desde TypeScript 2.9 cuando apunta a formatos de módulos compatibles, vea la nota )

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

Está declarando que todos los módulos que tienen un especificador que termina en .jsontienen una única exportación denominada default .

Hay varias formas en que puede consumir correctamente un módulo como

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

y

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

y

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

y

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

La primera forma es la mejor y el azúcar sintáctico que aprovecha es la razón por la que JavaScript tiene default exporta.

Sin embargo, mencioné las otras formas para darle una pista sobre lo que está mal. Presta especial atención al último. requirele da un objeto que representa el módulo en sí y no sus enlaces exportados.

Entonces, ¿por qué el error? Porque tu escribiste

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

Y sin embargo, no hay una exportación nombrada primaryMaindeclarada por su"*.json" .

Todo esto supone que su cargador de módulos proporciona el JSON como la defaultexportación como lo sugiere su declaración original.

Nota: desde TypeScript 2.9, puede usar el --resolveJsonModuleindicador del compilador para que TypeScript analice los .jsonarchivos importados y proporcione información correcta sobre su forma, evitando la necesidad de una declaración de módulo comodín y validando la presencia del archivo. Esto no es compatible con ciertos formatos de módulos de destino.

Aluan Haddad
fuente
1
@Royi que depende de su cargador. Para archivos remotos, considere usarawait import('remotepath');
Aluan Haddad el
27
Sigue desplazándote, la respuesta más actualizada a continuación.
jbmusso
@jbmusso Agregué información sobre las mejoras introducidas por versiones posteriores de TypeScript, pero no creo que esta respuesta esté desactualizada porque es conceptual. Sin embargo, estoy abierto a sugerencias para mejoras adicionales.
Aluan Haddad
1
El riesgo es que algunas personas simplemente puedan copiar / pegar las primeras líneas de su respuesta, solo arreglando el síntoma y no la causa raíz. Creo que la respuesta de @ kentor produce mayores detalles y proporciona una respuesta más completa. Una recomendación sería mover su Nota sobre su respuesta, indicando claramente que esta es la forma correcta de abordar este problema a partir de hoy.
jbmusso
@Atombit obviamente ha funcionado para muchas personas, incluyéndome a mí. ¿Le gustaría explicar qué no funciona antes de rechazar la respuesta aceptada?
Aluan Haddad
267

Con TypeScript 2.9. + Simplemente puede importar archivos JSON con typesafety e intellisense como este:

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

Asegúrese de agregar esta configuración en la compilerOptionssección de su tsconfig.json( documentación ):

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

Notas al margen:

  • Typecript 2.9.0 tiene un error con esta función JSON, se corrigió con 2.9.2
  • EsModuleInterop solo es necesario para la importación predeterminada de colorsJson. Si lo deja en falso, debe importarlo conimport * as colorsJson from '../colors.json'
kentor
fuente
17
No necesariamente lo necesita esModuleInterop, pero debe hacerlo import * as foo from './foo.json';: esModuleInteropestaba causando otros problemas para mí cuando intenté habilitarlo.
mpen
1
Tienes razón, debería haber agregado eso como una nota al margen :-).
kentor
10
Nota: La opción "resolveJsonModule" no se puede especificar sin la estrategia de resolución del módulo "nodo", por lo que también debe incluirla "moduleResolution": "node"en su tsconfig.json. También viene con la desventaja de que los *.jsonarchivos que desea importar deben estar dentro "rootDir". Fuente: blogs.msdn.microsoft.com/typescript/2018/05/31/…
Benny Neugebauer
2
@mpen eso es correcto pero import * as foo from './foo.json'es el formulario de importación incorrecto. Debería ser import foo = require('./foo.json');cuando no se usaesModuleInterop
Aluan Haddad
1
La única parte que necesitaba era "resolveJsonModule": truey todo está bien
Michael Elliott
10

Es fácil de usar la versión de mecanografiado 2.9+. Por lo tanto, puede importar fácilmente archivos JSON como @kentor describe .

Pero si necesita usar versiones anteriores:

Puede acceder a los archivos JSON de manera más TypeScript. Primero, asegúrese de que su nueva typings.d.tsubicación sea la misma que con la includepropiedad en su tsconfig.jsonarchivo.

Si no tiene una propiedad de inclusión en su tsconfig.jsonarchivo. Entonces la estructura de su carpeta debería ser así:

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

Pero si tiene una includepropiedad en su tsconfig.json:

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

Entonces typings.d.tsdebería estar en el srcdirectorio como se describe en la includepropiedad

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

Como en muchas de las respuestas, puede definir una declaración global para todos sus archivos JSON.

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

pero prefiero una versión más tipada de esto. Por ejemplo, supongamos que tiene un archivo de configuración config.jsoncomo ese:

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

Entonces podemos declarar un tipo específico para ello:

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

Es fácil importar en sus archivos de mecanografiado:

import * as Config from 'config.json';

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

Pero en la fase de compilación, debe copiar los archivos JSON a su carpeta dist manualmente. Solo agrego una propiedad de script a mi package.jsonconfiguración:

{
    "name"   : "some project",
    "scripts": {
        "build": "rm -rf dist && tsc && cp src/config.json dist/"
    }
}
Fırat KÜÇÜK
fuente
¿Es rm -rf algo de Linux / Unix, o funcionará también en el viejo Windurz?
Cody
gracias, mi tipings.d.ts estaba fuera de lugar. Tan pronto como me mudé a / src, el mensaje de error desapareció.
Gi1ber7
1
@Cody De hecho, es solo una cosa de Linux / Unix.
Maxie Berkmann
7

En su archivo de definición de TS, por ejemplo typings.d.ts`, puede agregar esta línea:

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

Luego agregue esto en su archivo de mecanografiado (.ts):

import * as data from './colors.json';
const word = (<any>data).name;
Mehadi Hassan
fuente
2
Esta es una muy mala idea.
Aluan Haddad
3
¿te importaría explicar por qué es malo? No soy experto en mecanografiado. @AluanHaddad
Mehadi Hassan
55
Su afirmación tipo de anydice dos cosas. 1) que has declarado o importado incorrectamente en la cara simplemente por definición. Su deberían Nunca , bajo ninguna circunstancia hace falta colocar una afirmación de tipo en una importación de un módulo que haya declarado a sí mismo. 2) incluso si tienes un cargador loco que de alguna manera resuelve esto en tiempo de ejecución, Dios no lo quiera, aún sería una forma increíblemente confusa y la más frágil de acceder a un módulo de la forma dada. * as x fromy x fromson aún más incompatibles en tiempo de ejecución que lo que está en el OP. En serio no hagas esto.
Aluan Haddad
55
Gracias por su respuesta. Lo he entendido claramente. @AluanHaddad
Mehadi Hassan
2

Otra forma de ir

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

Esto todavía puede definir el tipo json si lo desea y no tiene que usar comodines.

Por ejemplo, tipo personalizado json.

interface User {
  firstName: string;
  lastName: string;
  birthday: Date;
}
const user: User = require('./user.json');
Sr. Br
fuente
2
Esto no tiene nada que ver con la pregunta y también es una mala práctica.
Aluan Haddad
0

A menudo, en las aplicaciones Node.js se necesita un .json. Con TypeScript 2.9, --resolveJsonModule permite importar, extraer tipos y generar archivos .json.

Ejemplo #

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

Ruben Palavecino
fuente