Importando arquivo json no TypeScript

146

Eu tenho um JSONarquivo que se parece com o seguinte:

{

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

Estou tentando importá-lo para um .tsxarquivo. Para isso, adicionei isso à definição de tipo:

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

E eu estou importando assim.

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

E no arquivo, eu uso a cor primaryMaincomo colors.primaryMain. No entanto, recebo um erro:

A propriedade 'primaryMain' não existe no tipo 'typeof "* .json"

Sooraj
fonte
3
Sua declaração de módulo e seu formulário de importação discordam.
Aluan Haddad
2
Você se importa de mostrar um exemplo? Eu sou datilografado noob.
precisa saber é o seguinte

Respostas:

93

O formulário de importação e a declaração do módulo precisam concordar sobre a forma do módulo, sobre o que ele exporta.

Quando você escreve (uma prática subótima para importar JSON desde o TypeScript 2.9 ao direcionar formatos de módulos compatíveis, consulte a nota )

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

Você está declarando que todos os módulos que possuem um especificador terminado em .jsontêm uma única exportação denominada default .

Existem várias maneiras de consumir corretamente esse módulo, incluindo

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

A primeira forma é a melhor e o açúcar sintático que ela aproveita é o motivo pelo qual o JavaScript defaultexporta.

No entanto, mencionei as outras formas para lhe dar uma dica sobre o que está acontecendo de errado. Preste atenção especial ao último. requirefornece um objeto que representa o próprio módulo e não suas ligações exportadas.

Então, por que o erro? Porque você escreveu

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

E, no entanto, não há exportação nomeada primaryMaindeclarada pelo seu "*.json".

Tudo isso pressupõe que o carregador de módulos esteja fornecendo o JSON como defaultexportação, conforme sugerido por sua declaração original.

Nota: Desde o TypeScript 2.9, você pode usar o --resolveJsonModulesinalizador do compilador para que o TypeScript analise os .jsonarquivos importados e forneça as informações corretas sobre sua forma, evitando a necessidade de uma declaração do módulo curinga e validando a presença do arquivo. Isso não é suportado para determinados formatos de módulo de destino.

Aluan Haddad
fonte
1
@ Royi que depende do seu carregador. Para arquivos remotos considerar o usoawait import('remotepath');
Aluan Haddad
26
Mantenha a rolagem, resposta mais atualizada abaixo.
Jbmusso # 24/19
@jbmusso Adicionei algumas informações sobre as melhorias introduzidas por versões posteriores do TypeScript, mas não acho que essa resposta esteja desatualizada porque é conceitual. No entanto, estou aberto a sugestões para novas melhorias.
Aluan Haddad 24/05/19
1
O risco é que algumas pessoas possam simplesmente copiar / colar as primeiras linhas da sua resposta, apenas corrigindo o sintoma e não a causa raiz. Acredito que a resposta do @ kentor produz mais detalhes e fornece uma resposta mais completa. Uma recomendação seria passar sua nota para cima da sua resposta, afirmando claramente que esta é a maneira correta de resolver esse problema a partir de hoje.
Jbmusso
@ Atombit, obviamente, funcionou para muitas pessoas, inclusive eu. Importa-se de explicar o que não está funcionando antes de reduzir a resposta aceita?
Aluan Haddad 16/03
267

Com o TypeScript 2.9. +, Você pode simplesmente importar arquivos JSON com segurança de tipo e inteligência como esta:

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

Certifique-se de adicionar essas configurações na compilerOptionsseção da tsconfig.json( documentação ):

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

Notas laterais:

  • O Typcript 2.9.0 possui um bug com esse recurso JSON, foi corrigido com o 2.9.2
  • O esModuleInterop é necessário apenas para a importação padrão do colorsJson. Se você deixar definido como false, precisará importá-lo comimport * as colorsJson from '../colors.json'
kentor
fonte
17
Você não precisa necessariamente esModuleInterop, mas precisa fazer isso import * as foo from './foo.json';- esModuleInteropestava causando outros problemas para mim quando tentei ativá-lo.
MJ #
1
Você está certo, eu deveria ter acrescentado isso como uma nota lateral :-).
Kentor
10
Nota: A opção "resolveJsonModule" não pode ser especificada sem a estratégia de resolução do módulo "node", portanto, você também precisa colocá-la "moduleResolution": "node"no seu tsconfig.json. Também vem com a desvantagem de que os *.jsonarquivos que você deseja importar precisam estar dentro "rootDir". Fonte: blogs.msdn.microsoft.com/typescript/2018/05/31/…
Benny Neugebauer
2
@pen está correto, mas import * as foo from './foo.json'é o formulário de importação errado. Deve ser import foo = require('./foo.json');quando não estiver usandoesModuleInterop
Aluan Haddad
1
Apenas uma parte que eu precisava era "resolveJsonModule": truee está tudo bem
Michael Elliott
10

É fácil usar a versão datilografada 2.9+. Assim, você pode importar facilmente arquivos JSON como o @kentor descrito .

Mas se você precisar usar versões mais antigas:

Você pode acessar arquivos JSON de maneira mais TypeScript. Primeiro, verifique se o seu novo typings.d.tslocal é igual ao da includepropriedade no seu tsconfig.jsonarquivo.

Se você não possui uma propriedade include no seu tsconfig.jsonarquivo. Então sua estrutura de pastas deve ser assim:

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

Mas se você tem uma includepropriedade no seu tsconfig.json:

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

Então você typings.d.tsdeve estar no srcdiretório conforme descrito na includepropriedade

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

Como em muitas respostas, você pode definir uma declaração global para todos os seus arquivos JSON.

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

mas eu prefiro uma versão mais digitada disso. Por exemplo, digamos que você tenha um arquivo de configuração config.jsonassim:

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

Em seguida, podemos declarar um tipo específico para ele:

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

É fácil importar nos seus arquivos datilografados:

import * as Config from 'config.json';

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

Mas na fase de compilação, você deve copiar os arquivos JSON para a pasta dist manualmente. Acabei de adicionar uma propriedade de script à minha package.jsonconfiguração:

{
    "name"   : "some project",
    "scripts": {
        "build": "rm -rf dist && tsc && cp src/config.json dist/"
    }
}
Fırat KÜÇÜK
fonte
O rm -rf é uma coisa do Linux / Unix, ou isso funcionará também no Windurz?
Cody
obrigado, meu typings.d.ts estava fora do lugar. Assim que mudei para / src, a mensagem de erro desapareceu.
Gi1ber7 7/10/19
1
@ Cody É realmente apenas uma coisa Linux / Unix.
Maxie Berkmann 19/01
7

No seu arquivo TS Definition, por exemplo, typings.d.ts`, você pode adicionar esta linha:

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

Em seguida, adicione isso no seu arquivo datilografado (.ts): -

import * as data from './colors.json';
const word = (<any>data).name;
Mehadi Hassan
fonte
2
Esta é uma péssima ideia.
Aluan Haddad
3
você se importaria de explicar por que isso é ruim ??? Eu não sou especialista em texto datilografado. @AluanHaddad
Mehadi Hassan
5
Sua afirmação de tipo anydiz duas coisas. 1) que você declarou ou importou incorretamente, simplesmente por definição. Você nunca deve, em circunstância alguma, precisar colocar uma declaração de tipo na importação de um módulo que você mesmo declarou. 2) mesmo que você tenha um carregador insano que de alguma forma funcione em tempo de execução, é proibido pelos deuses, ainda assim seria uma maneira incrivelmente confusa e mais frágil de acessar um módulo com a forma especificada. * as x frome x fromtêm um tempo de execução ainda mais incompatível do que o que está no OP. Sério, não faça isso.
Aluan Haddad
5
Obrigado pela sua resposta. Eu entendi claramente. @AluanHaddad
Mehadi Hassan
2

Outro caminho a percorrer

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

Foi assim que você ainda pode definir o tipo de json que deseja e não precisa usar curinga.

Por exemplo, tipo personalizado json.

interface User {
  firstName: string;
  lastName: string;
  birthday: Date;
}
const user: User = require('./user.json');
Mr Br
fonte
2
Isso não tem nada a ver com a pergunta e também é uma prática ruim.
Aluan Haddad
0

Geralmente, nos aplicativos Node.js, é necessário um .json. Com o TypeScript 2.9, --resolveJsonModule permite importar, extrair tipos e gerar arquivos .json.

Exemplo #

// 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
fonte