クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【TypeScript】外部のJSONファイルを型付で読み込む

メタ言語の続きです。前回はgulpでwebpackを起動してモジュールをバンドルしました。今回は、その中のデータ部分のモジュールを外部JSONファイルにして、それをTypeScriptで読み込みます。データに型も設定します。それではいきましょう!

【目次】

※参考:前回記事
【TypeScript】gulpでwebpackを起動できるようにする(モジュールも再分割) - クモのようにコツコツと

※参考:【メタ言語】HTMLテンプレートエンジン、AltCSS、AltJSのまとめ
qiita.com

前回のおさらい

gulpでwebpackを起動してモジュールをバンドルする。 https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20200720/20200720194200.jpg

詳細は前回の記事を参照。

※参考:【TypeScript】gulpでwebpackを起動できるようにする(モジュールも再分割) - クモのようにコツコツと

データのモジュール(unsei.ts)

前回作ったTypeScriptモジュールのうち、下記の「unsei.ts」はデータしか入ってない。

// 運勢
export const unsei: string[] = [ 
    "大吉",
    "吉",
    "中吉",
    "小吉",
    "末吉",
    "凶",
    "大凶"
];
  • 変数unseiをエクスポート(型は文字列の配列string[]
  • 変数unseiの中身は配列ですべて文字列

これは外部に引っ張りだしてJSONファイル化したい。その方がファイル構成的な見通しもよくなる。

JSONファイルには型の設定を含めないため、TypeScript側で後から型を設定する必要がある。

これは何かのAPIを叩いて返ってきたJSONデータを処理する、などのシミュレーションでもある。

データのモジュールをJSONファイル化(unsei.json)

データのモジュール「unsei.ts」を「json」フォルダに移動し「unsei.json」にリネイム。

f:id:idr_zz:20201120060751j:plain

unsei.jsonをJSON形式の書式に変更

{
    "unsei": [ 
    "大吉",
    "吉",
    "中吉",
    "小吉",
    "末吉",
    "凶",
    "大凶"
    ]
}
  • 全体を連想配列に入れる。変数名unseiはキー名
  • export、変数名、型指定などは削除

jsonフォルダは以前、EJSで読み込んだ本文テキストdata.jsonを入れたフォルダ。

※参考:【EJS】fsモジュールを使ってコンテンツのJSONデータを読み込む - クモのようにコツコツと

tsconfig.jsonでJSONファイルの読み込み設定

TypeScrptでJSONファイルをインポートしようとしたらデフォルトではコンパイルエラーになった。

TypeScriptの設定ファイル「tsconfig.json」で下記の設定を有効にする必要がある。

{
  "compilerOptions": {
    /* 中略 */
    "moduleResolution": "node",
    "resolveJsonModule": true
  }
}
  • moduleResolutionキーの値をnode
  • resolveJsonModuleキーの値をtrue

この2つの設定によってJSONが自動的に定義された型付でインポートされる(自分の場合、型設定はうまくいかなかった)。

また、*.d.tsを作るとキーごとに個別に型が設定できるとのこと!(後述するが、今回は全体的に文字列にした)

※参考:TypeScriptでJSONファイルを型付きで読み込む - Qiita

JSONファイルをインポート(kekka.ts)

「unsei.ts」のデータをインポートしていたモジュールは「kekka.ts」

kekka.ts修正前

import { unsei } from "./unsei";

// 結果
export let kekka = (): string => {
    let nmb: number = Math.floor(Math.random() * unsei.length );
    return unsei[nmb]; 
}

同一階層のunsei.tsunseiモジュールとしてインポート

kekka.ts修正後

import Unsei from "../../../json/unsei.json";

const unsei: string[] = Unsei.unsei;

// 結果
export let kekka = (): string => {
    let nmb: number = Math.floor(Math.random() * unsei.length );
    return unsei[nmb]; 
}
  • jsonフォルダのunsei.jsonUnseiモジュールとしてインポート
  • 変数unseiに文字列配列型を指定し、Unseiオブジェクトのunseiキーを取得

冒頭のインポートの書き方は{ unsei }だとコンパイルエラーになった。

* as Unsei Unseiに変えたのは下記を参考にして。(※11/23追記:Warningが出てたので* asは削除)

※参考:angular - Importing json; Typescript error: Module has no exported member - Stack Overflow

※参考:私的TypeScriptとの関わり方ガイドライン - 角待ちは対空

インポートの書き方はこちらを参照

※参考:ファイルモジュールの詳細 - TypeScript Deep Dive 日本語版

JSONファイルをインポートするだけだと「型がありません」というコンパイルエラーになるので、変数unseiで型を設定する。

const unsei: string[] = Unsei.unsei;

また、取得したいのはUnsei全体ではなくunseiキーの値の配列なのでそれも合わせて設定(それによって処理部分は書き換えずに済む)

gulpfileのwatch設定を変更(JSONファイル追加)※2020/11/21追加

あとから気がついたのだが、gulpの設定ファイルgulpファイルでJSに関してはTypeScriptのtsファイルしか監視してなかったため、JSONファイルを編集してもコンパイルやブラウザ自動更新(ホットリロード)が動かなかった。

TypeScriptのwatch設定を下記のように変更したらJSONファイルも監視された。

// 監視ファイル
gulp.task('watch-files', (done) =>  {
    gulp.watch(["./src/ejs/**/*.ejs", "./src/json/**/*.json"], gulp.task('ejs'));
    gulp.watch("./dest/*.html", gulp.task('browser-reload'));
    gulp.watch("./src/scss/**/*.scss", gulp.task('sass'));
    gulp.watch("./dest/css/*.css", gulp.task('browser-reload'));
    gulp.watch(["./src/ts/**/*.ts", "./src/json/**/*.json"], gulp.task('webpack')); // ここを変更
    gulp.watch("./dest/js/*.js", gulp.task('browser-reload'));
    done();
});
  • gulp.watch()でtsフォルダの.tsファイル、またはjsonフォルダの.jsonフォルダが変更されたらwebpackを実行

以前、EJSで設定した書き方と同じ

※参考:【EJS】fsモジュールを使ってコンテンツのJSONデータを読み込む - クモのようにコツコツと

動作確認

gulpを起動

$ npx gulp

localhost の3000が起動した! f:id:idr_zz:20201120080530j:plain

ボタンを押すとおみくじのアラートが開く! f:id:idr_zz:20201120080543j:plain

バンドルされた「script.js」

!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e){e.exports=JSON.parse('{"a":["大吉","吉","中吉","小吉","末吉","凶","大凶"]}')},function(e,t,n){"use strict";n.r(t);var r=n(0).a,o=function(){var e,t="あなたの運勢は「"+(e=Math.floor(Math.random()*r.length),r[e]+"」です!!");alert(t)};document.addEventListener("DOMContentLoaded",(function(){document.querySelector(".inner__text-btn").addEventListener("click",o,!1)}),!1)}]);

JSONファイルの内容がSON.parse()で読み込まれている。

今回の修正内容(GitHub)

※参考:TypeScriptでJSONファイル読み込む · ryo-i/frontendMetaLanguage@c304c86 · GitHub

追加修正(watch設定)

※参考:frontendMetaLanguage/gulpfile.js at 9f4bb8667f887d70f845f0fab1eef22538c91415 · ryo-i/frontendMetaLanguage · GitHub

プレビュー画面(GitHub Pages)

※参考:メタ言語同時コンパイル(EJS、Sass(SCSS)、TypeScript)

最後に

ということで、TypeScriptでJSONファイルを読み込んで型を設定できました。これによってデータ部分を外部に取り出せてファイルの見通しが良くなります。外部のAPIを叩いて返ってきたデータを読み込む、という処理もたぶん同じ感じでできるように思います。

これからやってみたいのはExpressのAPI環境とこのメタ言語環境の融合です。他にもいくつかやってみたいことがあるので、段階的に進めていきます。

それではまた!


※参考:【メタ言語】HTMLテンプレートエンジン、AltCSS、AltJSのまとめ
qiita.com