Імпорт файлу json у TypeScript


147

У мене є 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"


3
Ваша декларація модуля та форма імпорту не погоджуються.
Алуан Хаддад

2
Ви не проти показувати приклад? Я машинопис noob.
Сурадж

Можливий дублікат помилки компілятора
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
@ Рой, що залежить від вашого завантажувача. Для віддалених файлів розгляньте можливість використанняawait import('remotepath');
Aluan Haddad

28
Продовжуйте прокручувати, більш актуальну відповідь нижче.
jbmusso

@jbmusso Я додав інформацію про вдосконалення, внесені пізнішими версіями TypeScript, але не думаю, що ця відповідь застаріла, оскільки вона концептуальна. Однак я відкритий для пропозицій щодо подальших вдосконалень.
Алуан Хаддад

1
Ризик полягає в тому, що деякі люди можуть просто скопіювати / вставити перші рядки вашої відповіді, виправляючи лише симптом, а не першопричину. Я вважаю, що відповідь @ kentor дає більш детальну інформацію та дає більш повну відповідь. Рекомендацією є перенесення Вашої Записки поверх своєї відповіді, чітко заявляючи, що це правильний спосіб вирішити цю проблему на сьогодні.
jbmusso

@Atombit, очевидно, працював для багатьох людей, включаючи мене. Потрібно пояснити, що не працює, перш ніж спростувати прийняту відповідь?
Алуан Хаддад

269

З TypeScript 2.9. + Ви можете просто імпортувати файли JSON з безпекою та інтелігенцією типу:

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 необхідний лише для імпорту кольорів за замовчуванням. Якщо ви встановите значення false, тоді вам потрібно імпортувати йогоimport * as colorsJson from '../colors.json'

17
Вам не обов’язково потрібно esModuleInterop, але тоді ви повинні робити import * as foo from './foo.json';- це esModuleInteropспричиняло інші проблеми для мене, коли я намагався це ввімкнути.
mpen

1
Ви маєте рацію, я мав би додати це як бічну примітку :-).
Кентор

10
Примітка. Опцію "resolutionJsonModule" неможливо вказати без стратегії вирішення модуля "node", тому вам також потрібно ввести "moduleResolution": "node"свою tsconfig.json. З іншого боку, це те, що *.jsonфайли, які ви хочете імпортувати, повинні бути всередині "rootDir". Джерело: blogs.msdn.microsoft.com/typescript/2018/05/31/…
Бенні Нойгебауер

2
@mpen це правильно, але import * as foo from './foo.json'це неправильна форма імпорту. Це має бути, import foo = require('./foo.json');коли не використовуєтьсяesModuleInterop
Алуан Хаддад

1
Тільки потрібна мені частина була, "resolveJsonModule": trueі все добре
Майкл Елліотт

10

Використовувати машинопис версії 2.9 і новішої версії просто. Таким чином, ви можете легко імпортувати файли JSON у порядку, визначеному @kentor .

Але якщо вам потрібно використовувати більш старі версії:

Ви можете отримати доступ до файлів JSON більш типовим способом. По-перше, переконайтесь, що ваше нове typings.d.tsмісцезнаходження збігається із includeвластивістю у вашому tsconfig.jsonфайлі.

Якщо у вас немає властивості включити у свій 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/"
    }
}

Чи є rm -rf річ Linux / Unix, чи це також буде працювати на ol 'Windurz?
Коді

дякую, мій typing.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
Це дуже погана ідея.
Алуан Хаддад

3
Чи не заперечуєте, будь ласка, поясніть, чому це погано ??? Я не знаю машинопис. @AluanHaddad
Мехаді Хассан

5
Твоє твердження anyговорить про дві речі. 1) що ви неправильно заявили чи імпортували на обличчя, просто за визначенням. Ви повинні ніколи ні за яких обставин потрібно помістити твердження типу на імпорт модуля ви самі заявили. 2) навіть якщо у вас є божевільний навантажувач, який якось спрацьовує це під час виконання, боги забороняють, все одно це буде шалено заплутаний і самий крихкий спосіб отримати доступ до модуля заданої форми. * as x fromі x fromнавіть більше розумний час виконання, ніж те, що є в ОП. Серйозно не робіть цього.
Алуан Хаддад

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
Це не має нічого спільного з питанням, а також є поганою практикою.
Алуан Хаддад

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.