Импорт файла json в TypeScript

148

У меня есть JSONфайл, который выглядит следующим образом:

{

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

Пытаюсь импортировать в .tsxфайл. Для этого я добавил это в определение типа:

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

И я импортирую это вот так.

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

И в файле я использую цвет primaryMainкак colors.primaryMain. Однако я получаю сообщение об ошибке:

Свойство primaryMain не существует для типа typeof "* .json"

Sooraj
источник
3
Объявление вашего модуля и форма импорта не согласуются.
Алуан Хаддад
2
Не могли бы вы показать пример? Я машинописный нуб.
Sooraj
Возможный дубликат ошибки компилятора
Typescript

Ответы:

93

Форма импорта и объявление модуля должны согласовывать форму модуля и то, что он экспортирует.

Когда вы пишете (субоптимальная практика для импорта JSON с TypeScript 2.9 при ориентации на совместимые форматы модулей, см. Примечание )

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

Вы заявляете, что все модули, у которых спецификатор заканчивается на, .jsonимеют один экспорт с именем default .

Есть несколько способов правильно использовать такой модуль, включая

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

и

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

и

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

и

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

Первая форма является лучшей, и синтаксический сахар, который она использует, является той самой причиной, по которой JavaScript имеет defaultэкспорт.

Однако я упомянул другие формы, чтобы подсказать вам, что происходит не так. Обратите особое внимание на последнюю. requireдает вам объект, представляющий сам модуль, а не его экспортируемые привязки.

Так почему ошибка? Потому что ты написал

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

И все же экспорт, указанный primaryMainвашим "*.json".

Все это предполагает, что ваш загрузчик модуля предоставляет JSON в качестве defaultэкспорта, как это было предложено вашим исходным объявлением.

Примечание. Начиная с TypeScript 2.9, вы можете использовать --resolveJsonModuleфлаг компилятора, чтобы TypeScript анализировал импортированные .jsonфайлы и предоставлял правильную информацию об их форме, устраняя необходимость в объявлении модуля с подстановочными знаками и проверяя наличие файла. Это не поддерживается для определенных форматов целевых модулей.

Алуан Хаддад
источник
1
@Royi, что зависит от вашего загрузчика. Для удаленных файлов рассмотрите возможность использованияawait import('remotepath');
Алуан Хаддад
28
Продолжайте прокручивать, более свежий ответ ниже.
jbmusso
@jbmusso Я добавил некоторую информацию об улучшениях, внесенных в более поздние версии TypeScript, но я не думаю, что этот ответ устарел, потому что он концептуален. Однако я открыт для предложений по дальнейшим улучшениям.
Алуан Хаддад
1
Риск заключается в том, что некоторые люди могут просто скопировать / вставить первые строки вашего ответа, исправляя только симптом, а не основную причину. Я считаю, что ответ @kentor дает более подробную информацию и дает более полный ответ. Рекомендуется переместить заметку поверх ответа, четко указав, что это правильный способ решения этой проблемы на сегодняшний день.
jbmusso
@Atombit, очевидно, сработал для многих людей, включая меня. Не хотите ли объяснить, что не работает, прежде чем голосовать против принятого ответа?
Алуан Хаддад,
270

С TypeScript 2.9. + Вы можете просто импортировать файлы JSON с типовой безопасностью и intellisense следующим образом:

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

Обязательно добавьте эти настройки в compilerOptionsраздел вашей tsconfig.json( документации ):

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

Примечания стороны:

  • В Typescript 2.9.0 есть ошибка с этой функцией JSON, она была исправлена ​​в 2.9.2
  • EsModuleInterop необходим только для импорта по умолчанию colorsJson. Если вы оставите для него значение false, вам придется импортировать его с помощьюimport * as colorsJson from '../colors.json'
Kentor
источник
18
Вам не обязательно нужно esModuleInterop, но вы должны сделать import * as foo from './foo.json';- это esModuleInteropвызывало у меня другие проблемы, когда я пытался его включить.
mpen
1
Вы правы, я должен был добавить это в качестве примечания :-).
kentor 01
10
Примечание. Параметр «resolveJsonModule» не может быть указан без стратегии разрешения модуля «node», поэтому вам также необходимо добавить "moduleResolution": "node"в свой tsconfig.json. У него также есть обратная сторона: *.jsonфайлы, которые вы хотите импортировать, должны находиться внутри "rootDir". Источник: blogs.msdn.microsoft.com/typescript/2018/05/31/…
Benny Neugebauer
2
@mpen это правильно, но import * as foo from './foo.json'это неправильная форма импорта. Он должен быть, import foo = require('./foo.json');когда не используетсяesModuleInterop
Aluan Haddad
1
Только часть, которая мне нужна была, "resolveJsonModule": trueи все хорошо
Майкл Эллиотт
10

Машинопись версии 2.9+ проста в использовании. Таким образом, вы можете легко импортировать файлы JSON, как описано в @kentor .

Но если вам нужно использовать более старые версии:

Вы можете получить доступ к файлам JSON более типичным способом. Во-первых, убедитесь, что ваше новое typings.d.tsместоположение такое же, как и includeсвойство в вашем tsconfig.jsonфайле.

Если у вас нет свойства include в вашем tsconfig.jsonфайле. Тогда ваша структура папок должна быть такой:

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

Но если у вас есть includeнедвижимость в вашем tsconfig.json:

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

Затем вы typings.d.tsдолжны быть в srcкаталоге, как описано в includeсвойстве

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

Как и во многих ответах, вы можете определить глобальное объявление для всех ваших файлов JSON.

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

но я предпочитаю более типизированную версию этого. Например, допустим, у вас есть config.jsonтакой файл конфигурации :

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

Затем мы можем объявить для него определенный тип:

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

Легко импортировать в файлы машинописного текста:

import * as Config from 'config.json';

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

Но на этапе компиляции вам следует вручную скопировать файлы JSON в папку dist. Я просто добавляю свойство скрипта в свою package.jsonконфигурацию:

{
    "name"   : "some project",
    "scripts": {
        "build": "rm -rf dist && tsc && cp src/config.json dist/"
    }
}
Fırat KÜÇÜK
источник
Является ли rm -rf частью Linux / Unix, или это будет работать и на старом Windurz?
Коди
спасибо, мой typings.d.ts оказался неуместным. Как только я перешел в / src, сообщение об ошибке исчезло.
Gi1ber7
1
@Cody Это действительно только вещь Linux / Unix.
Макси Беркманн,
7

В вашем файле определения TS, например typings.d.ts`, вы можете добавить эту строку:

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

Затем добавьте это в свой файл машинописного текста (.ts): -

import * as data from './colors.json';
const word = (<any>data).name;
Мехади Хасан
источник
2
Это очень плохая идея.
Aluan Haddad
3
не могли бы вы объяснить, почему это плохо ??? Я не разбираюсь в машинописном тексте. @AluanHaddad
Мехади Хассан
5
Ваше типичное утверждение anyговорит о двух вещах. 1), который вы объявили или импортировали неправильно просто по определению. Вам ни при каких обстоятельствах не следует размещать утверждение типа при импорте модуля, который вы сами объявили. 2) даже если у вас есть сумасшедший загрузчик, который каким-то образом работает с этим во время выполнения, не дай бог, это все равно будет безумно запутанным и самым хрупким способом доступа к модулю данной формы. * as x fromи x fromдаже более несовместимы во время выполнения, чем то, что находится в OP. Серьезно не делай этого.
Aluan Haddad
5
Благодарю за ваш ответ. Я ясно понял. @AluanHaddad
Мехади Хассан
2

Другой способ пойти

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

При этом вы все еще можете определить тип json, если хотите, и не должны использовать подстановочные знаки.

Например, пользовательский тип json.

interface User {
  firstName: string;
  lastName: string;
  birthday: Date;
}
const user: User = require('./user.json');
Г-н Бр
источник
2
Это не имеет ничего общего с вопросом и также является плохой практикой.
Aluan Haddad
0

Часто в приложениях Node.js требуется .json. В TypeScript 2.9 --resolveJsonModule позволяет импортировать, извлекать типы и создавать файлы .json.

Пример #

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

Рубен Палавесино
источник