Importowanie pliku JSON w TypeScript

166

Mam JSONplik, który wygląda następująco:

{

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

Próbuję zaimportować go do .tsxpliku. W tym celu dodałem to do definicji typu:

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

Importuję to w ten sposób.

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

W pliku używam koloru primaryMainjako colors.primaryMain. Jednak pojawia się błąd:

Właściwość „primaryMain” nie istnieje w typie „typeof„ * .json ”

Sooraj
źródło
3
Twoja deklaracja modułu i formularz importu nie zgadzają się.
Aluan Haddad
3
Czy możesz pokazać przykład? Jestem noobem na maszynie.
Sooraj
Możliwy duplikat błędu kompilatora
Typescript

Odpowiedzi:

112

Formularz importu i deklaracja modułu muszą być zgodne co do kształtu modułu, co eksportuje.

Podczas pisania (nieoptymalna praktyka importowania JSON od TypeScript 2.9 w przypadku kierowania na zgodne formaty modułów, patrz uwaga )

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

Twierdzisz, że wszystkie moduły, których specyfikator kończy się na, .jsonmają jeden nazwany eksport default.

Istnieje kilka sposobów prawidłowego wykorzystania takiego modułu, w tym

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

i

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

i

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

i

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

Pierwsza forma jest najlepsza, a cukier składniowy, z którego korzysta, jest przyczyną defaulteksportu JavaScript .

Jednak wspomniałem o innych formularzach, aby dać ci wskazówkę, co się dzieje. Zwróć szczególną uwagę na ostatnią. requiredaje obiekt reprezentujący sam moduł, a nie jego wyeksportowane powiązania.

Skąd więc błąd? Ponieważ napisałeś

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

A jednak nie ma żadnego primaryMainzadeklarowanego eksportu"*.json" .

Wszystko to zakłada, że ​​moduł ładujący moduł dostarcza JSON jako defaulteksport, zgodnie z sugestią zawartą w pierwotnej deklaracji.

Uwaga: Począwszy od TypeScript 2.9, możesz użyć --resolveJsonModuleflagi kompilatora, aby TypeScript analizował importowane .jsonpliki i dostarczał poprawne informacje dotyczące ich kształtu, eliminując potrzebę deklaracji modułu wieloznacznego i weryfikując obecność pliku. Nie jest to obsługiwane w przypadku niektórych formatów modułów docelowych.

Aluan Haddad
źródło
1
@Royi, to zależy od twojego ładowarki. W przypadku plików zdalnych rozważ użycieawait import('remotepath');
Aluan Haddad
46
Przewijaj dalej, bardziej aktualna odpowiedź poniżej.
jbmusso
@jbmusso Dodałem kilka informacji dotyczących ulepszeń wprowadzonych przez późniejsze wersje TypeScript, ale nie sądzę, aby ta odpowiedź była nieaktualna, ponieważ jest koncepcyjna. Jestem jednak otwarty na sugestie dotyczące dalszych ulepszeń.
Aluan Haddad
1
Istnieje ryzyko, że niektórzy ludzie mogą po prostu skopiować / wkleić pierwsze wiersze odpowiedzi, naprawiając tylko objaw, a nie przyczynę. Uważam, że odpowiedź @ kentor zawiera więcej szczegółów i zapewnia pełniejszą odpowiedź. Zalecane byłoby umieszczenie swojej notatki nad odpowiedzią, wyraźnie stwierdzając, że jest to właściwy sposób rozwiązania tego problemu na dzień dzisiejszy.
jbmusso
@Atombit to oczywiście zadziałało dla wielu osób, w tym dla mnie. Chcesz wyjaśnić, co nie działa, przed odrzuceniem zaakceptowanej odpowiedzi?
Aluan Haddad
303

Dzięki TypeScript 2.9. + Możesz po prostu importować pliki JSON z zabezpieczeniami typów i inteligencją w następujący sposób:

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

Pamiętaj, aby dodać te ustawienia w compilerOptionssekcji swojej tsconfig.json( dokumentacji ):

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

Uwagi dodatkowe:

  • Typescript 2.9.0 zawiera błąd związany z tą funkcją JSON, został on naprawiony w wersji 2.9.2
  • EsModuleInterop jest potrzebny tylko do domyślnego importu colorsJson. Jeśli pozostawisz to ustawione na false, musisz zaimportować je zimport * as colorsJson from '../colors.json'
kentor
źródło
22
Niekoniecznie potrzebujesz esModuleInterop, ale musisz to zrobić import * as foo from './foo.json';- esModuleInteroppowodowało to inne problemy, gdy próbowałem to włączyć.
mpen
2
Masz rację, powinienem dodać to na marginesie :-).
kentor
11
Uwaga: Opcji „replaceJsonModule” nie można określić bez strategii rozpoznawania modułu „węzeł”, więc należy również umieścić ją "moduleResolution": "node"w pliku tsconfig.json. Ma to również wadę, że *.jsonpliki, które chcesz zaimportować, muszą znajdować się wewnątrz "rootDir". Źródło: blogs.msdn.microsoft.com/typescript/2018/05/31/…
Benny Neugebauer
2
@mpen, to poprawne, ale import * as foo from './foo.json'nieprawidłowy formularz importu. Powinno być, import foo = require('./foo.json');gdy nie używaszesModuleInterop
Aluan Haddad
2
Należy to przyjąć jako właściwą odpowiedź, to najszybszy sposób na zrobienie tego
dr Freddy Dimethyltryptamine
11

Jest łatwy w użyciu na maszynie w wersji 2.9+. Możesz więc łatwo importować pliki JSON zgodnie z opisem @kentor .

Ale jeśli potrzebujesz użyć starszych wersji:

Możesz uzyskać dostęp do plików JSON w sposób bardziej TypeScript. Najpierw upewnij się, że nowa typings.d.tslokalizacja jest taka sama, jak w przypadku includewłaściwości w tsconfig.jsonpliku.

Jeśli nie masz właściwości include w swoim tsconfig.jsonpliku. Wtedy struktura folderów powinna wyglądać następująco:

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

Ale jeśli masz includenieruchomość w swoim tsconfig.json:

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

Następnie typings.d.tspowinieneś znajdować się w srckatalogu zgodnie z opisem we includewłaściwości

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

Jak w wielu odpowiedziach, możesz zdefiniować globalną deklarację dla wszystkich swoich plików JSON.

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

ale wolę bardziej wpisaną wersję tego. Na przykład, powiedzmy, że masz config.jsontaki plik konfiguracyjny :

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

Następnie możemy zadeklarować dla niego określony typ:

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

Łatwo jest zaimportować do plików maszynopisu:

import * as Config from 'config.json';

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

Jednak w fazie kompilacji należy ręcznie skopiować pliki JSON do folderu dist. Po prostu dodaję właściwość skryptu do mojej package.jsonkonfiguracji:

{
    "name"   : "some project",
    "scripts": {
        "build": "rm -rf dist && tsc && cp src/config.json dist/"
    }
}
Fırat KÜÇÜK
źródło
Czy rm -rf to rzecz Linux / Unix, czy też będzie działać na starym Windurzu?
Cody,
dziękuję, moje typings.d.ts były nie na miejscu. Jak tylko przeniosłem się do / src, komunikat o błędzie zniknął.
Gi1ber7
1
@Cody To rzeczywiście tylko kwestia Linuksa / Uniksa.
Maxie Berkmann
7

W swoim pliku TS Definition, np. Typings.d.ts`, możesz dodać tę linię:

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

Następnie dodaj to w swoim pliku maszynopisu (.ts): -

import * as data from './colors.json';
const word = (<any>data).name;
Mehadi Hassan
źródło
2
To bardzo zły pomysł.
Aluan Haddad
3
czy mógłbyś, proszę, wyjaśnić, dlaczego jest źle ??? Nie jestem ekspertem w maszynopisie. @AluanHaddad
Mehadi Hassan
5
Twoje stwierdzenie typu anymówi dwie rzeczy. 1), które zadeklarowałeś lub zaimportowałeś nieprawidłowo, po prostu z definicji. W żadnym wypadku nie powinieneś nigdy potrzebować asercji typu na imporcie modułu, który sam zadeklarowałeś. 2) nawet jeśli masz szalonego modułu ładującego, który jakoś działa w czasie wykonywania, nie daj Boże, nadal byłby to szalenie zagmatwany i najbardziej kruchy sposób na dostęp do modułu o danym kształcie. * as x fromi x fromsą jeszcze bardziej niedopasowane pod względem czasu wykonywania niż to, co jest w OP. Poważnie, nie rób tego.
Aluan Haddad
5
Dziękuję za odpowiedź. Zrozumiałem jasno. @AluanHaddad
Mehadi Hassan
3

Inny sposób

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

To było nadal możesz zdefiniować typ json, który chcesz i nie musisz używać symboli wieloznacznych.

Na przykład niestandardowy typ json.

interface User {
  firstName: string;
  lastName: string;
  birthday: Date;
}
const user: User = require('./user.json');
Pan Br
źródło
2
Nie ma to nic wspólnego z pytaniem i jest również złą praktyką.
Aluan Haddad
Zrobiłem to, ale otrzymuję daty jako ciągi znaków. Co należy zrobić, aby uzyskać właściwą deserializację obiektów daty z json?
Ε Г И І И О
1

Często w aplikacjach Node.js wymagany jest plik .json. W przypadku TypeScript 2.9 --resolveJsonModule umożliwia importowanie, wyodrębnianie typów i generowanie plików .json.

Przykład #

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

Ruben Palavecino
źródło
0

Włącz "resolveJsonModule": truew tsconfig.jsonpliku i zaimplementuj jak poniżej kod, to działa dla mnie:

const config = require('./config.json');
Dung Le
źródło