メタ言語の続きです。前回はfsモジュールを使ってEJSファイルでJSONファイルのデータを読み込みました。今回からはTypeScriptのモジュール分割編に入ります。以前作った「おみくじJS」をTypeScriptに移植し、モジュールファイルに分割してみます。それではいきましょう!
【目次】
- 前回のおさらい
- 前回までのTypeScript
- 以前作ったおみくじJS
- JSONデータのテキストを修正
- おみくじJSのコードを移植
- ボタンのSass(SCSS)を修正
- おみくじJSに型を付ける
- 型付きTypeScriptのコンパイル結果
- omikuji()関数をモジュール分割
- コンパイル結果…エラーになった。。
- 最後に
※参考:前回記事
【EJS】fsモジュールを使ってコンテンツのJSONデータを読み込む - クモのようにコツコツと
※参考:【メタ言語】HTMLテンプレートエンジン、AltCSS、AltJSのまとめ
qiita.com
前回のおさらい
前回まで作ったものこちら。
Sass(SCSS)とEJSをモジュール分割。さらにEJSはJSONファイルのデータから読み込んでテンプレートとデータの分離。
詳細はこちら
※参考:前回記事
【EJS】fsモジュールを使ってコンテンツのJSONデータを読み込む - クモのようにコツコツと
今回はTypeScriptのモジュール分割をやってみたい。
前回までのTypeScript
一番下の「ここ押せワンワン」ボタンを押すとアラートが出る。
ファイルは「script.ts」一つのみ。
「script.ts」のJSコード
document.addEventListener("DOMContentLoaded", () => { /* アロー関数 */ const hellow = () => { window.alert('hellow TypeScript??????????'); window.alert('こんにちは、型スクリプト'); window.alert(title + 'は第' + num + '回です。'); window.alert('おしまい'); } /* 変数(型付け) */ // let btn: Object; let btn = document.querySelector('.inner__text-btn'); let title: string; title = 'JS編'; let num: number; num = 3; /* イベント */ btn!.addEventListener('click', hellow, false); }, false);
詳細はこちちら
※参考:【gulp】Sass(SCSS)とTypeScriptを同時にコンパイルする環境を作る - クモのようにコツコツと
以前作ったおみくじJS
以前、Math.random()
を使って作ってみたおみくじ。
See the Pen Omikuji by イイダリョウ (@i_ryo) on CodePen.
「引く」ボタンを押すとランダムなおみくじ結果が出る。
JSコード
//要素取得 const omikuji = document.getElementById("omikuji"); //運気 const unki = [ "大吉", "吉", "中吉", "小吉", "末吉", "凶", "大凶" ] //結果 function kekka(){ const nmb = Math.floor(Math.random() * unki.length ); omikuji.innerHTML = unki[nmb]; } //イベント omikuji.addEventListener("click", kekka, false);
おみくじJSの詳細はこちら
※参考:【JS】Math.random()でつくるサイコロとおみくじ - クモのようにコツコツと
今回これをTypeScriptに移植して、さらにモジュール分割もしてみたい。
JSONデータのテキストを修正
まず画面に表示しているテキストを修正したい。
テキストのデータは「data.json」にある。これがEJSに読み込まれている。
{ "title":"TypeScriptで書いたクリックイベント", "text": "<button class='inner__text-btn'>ここ押せワンワン</button>" }
テキストを打ち替える。
{ "title":"TypeScriptで作ったおみくじ", "text": "<button class='inner__text-btn'>おみくじを引く!</button>" }
gulpを起動する
npx gulp
ブラウザが立ち上がると…
よっしゃ、テキストが打ち変わっている♪
EJSのJSONデータ読み込みの詳細はこちら
※参考:【EJS】fsモジュールを使ってコンテンツのJSONデータを読み込む - クモのようにコツコツと
おみくじJSのコードを移植
次におみくじJSのコードを移植する。
//運気 const unki = [ "大吉", "吉", "中吉", "小吉", "末吉", "凶", "大凶" ]; //結果 function kekka(){ const nmb = Math.floor(Math.random() * unki.length ); alert(unki[nmb]); } document.addEventListener("DOMContentLoaded", () => { /* 変数(型付け) */ // let btn: Object; let btn = document.querySelector('.inner__text-btn'); /* イベント */ btn!.addEventListener('click', kekka, false); }, false);
前半はおみくじJSからの移植。後半のクリックイベントにkekka()
関数を紐づけている。
おみくじボタンを押すと… よっしゃ!おみくじ結果が表示された!
ネイティブJSコードで書いてもそのまんま動くのがTypeScriptのいいところ♪*1
ボタンのSass(SCSS)を修正
ボタンを押すと青い枠線が出るのが気になったので修正したい。
ボタンのスタイルは「_inner.scss」モジュールにある。.inner-btn
セレクタ。
.inner { /* 中略 */ &-btn { background: $base-color; color: $text-color_w; padding: 10px; } } }
ここにスタイルを追記する。
.inner { /* 中略 */ &-btn { background: $base-color; color: $text-color_w; padding: 10px; border: none; border-radius: 5px; &:hover { cursor: pointer; opacity: 0.8; } &:focus { outline: none; } } }
border
のスタイルをなくし、border-radius
で角丸も付ける- 擬似クラス
:hover
でカーソルを指アイコン、不透明度も付ける - 擬似クラス
:focus
でoutline
で青線を無しに
擬似クラス:focus
でoutline:
プロパティの値を0
やnone
に変えると青線が消える。
※参考:[Chrome] ボタン要素の周りに表示される青線を消す - Qiita
青線が出なくなった!!
Sass(SCSS)のモジュール分割の詳細はこちら
※参考:【Sass(SCSS)】@importでBEMのBlockごとにファイル分割する - クモのようにコツコツと
※参考:【Sass(SCSS)】変数($)、@mixinを使ってモジュールを超えた共通スタイルを設定する - クモのようにコツコツと
おみくじJSに型を付ける
せっかくなのでおみくじJSにTypeScriptの型を付けてみる。
// 運勢 const unsei: string[] = [ "大吉", "吉", "中吉", "小吉", "末吉", "凶", "大凶" ]; // 結果 let kekka = (): string => { let nmb: number = Math.floor(Math.random() * unsei.length ); return unsei[nmb]; } // おみくじ let omikuji = (): void => { let massage: string = 'あなたの運勢は「' + kekka() + '」です。'; alert(massage); } // イベント実行 document.addEventListener("DOMContentLoaded", () => { let btn: HTMLButtonElement = document.querySelector('.inner__text-btn') as HTMLButtonElement; btn!.addEventListener('click', omikuji, false); }, false);
関数の構成を少し変えた。*2
kekka()
関数からアラートの部分をomikuji()
関数に分離kekka()
はreturn
でテキストを返すomikuji()
の変数massage
でメッセージ作成の処理を追加(この中でkekka()
を実行)- クリックイベントは
kekka
ではなくomikuji
を実行
TypeScriptの型を付けてみた。
- 変数
unsei
にstring[]
- アロー関数
kekka
にstring
- 変数
nmb
にnumber
- アロー関数
omikuji
にvoid
- 変数
massage
にstring
- DOM要素
btn
にHTMLButtonElement
(キャストも付ける)
型の種類は下記を参考にした。処理のみで戻り値return
がない関数はvoid
型になる。
※参考:TypeScriptの型システム - TypeScript Deep Dive 日本語版
DOM要素は種類によって「HTMLElement」を付ける(ボタンはHTMLButtonElement
など)。
※参考:TypeScriptでDOM要素を作成する | 株式会社CONFRAGE ITソリューション事業部
また、DOMには「キャスト」を書く必要があるみたい。
※参考:TypeScript - TypeScriptで型に関するエラーが解消できない。|teratail
「キャスト」とは型を変換すること。
キャストは値部分に追記する。2つの方法がある。
- 山カッコ
< >
で囲う as
で繋げる
山カッコはReactのJSXとカブるためas
が推奨とのこと。
※参考:Type Assertion(型アサーション) - TypeScript Deep Dive 日本語版
型付きTypeScriptのコンパイル結果
ブラウザの動作。アラートはちゃんと出てる!
コンパイル後の「script.js」
// 運勢 var unsei = [ "大吉", "吉", "中吉", "小吉", "末吉", "凶", "大凶" ]; // 結果 var kekka = function () { var nmb = Math.floor(Math.random() * unsei.length); return unsei[nmb]; }; // おみくじ var omikuji = function () { var massage = 'あなたの運勢は「' + kekka() + '」です。'; alert(massage); }; // イベント実行 document.addEventListener("DOMContentLoaded", function () { var btn = document.querySelector('.inner__text-btn'); btn.addEventListener('click', omikuji, false); }, false);
型などは全てなくなってES5の書き方になっている。
omikuji()関数をモジュール分割
さて、いよいよここからomikuji()関数をモジュール分割してみたい。
分割方法は「CommonJS」「AMD」などいろいろあるが基本的には「ESモジュール」でいいようだ。
※参考:ファイルモジュールの詳細 - TypeScript Deep Dive 日本語版
ESモジュールはこちらを参照。Vue.jsやReactのコンポーネントでもお馴染みな書き方。
※参考:【JS】モジュール(import / export)でどんなことができるのか事始めてみた - クモのようにコツコツと
まず「omikuji」フォルダとその中に「omikuji.ts」を作る。
「omikuji.ts」のコード
// 運勢 const unsei: string[] = [ "大吉", "吉", "中吉", "小吉", "末吉", "凶", "大凶" ]; // 結果 let kekka = (): string => { let nmb: number = Math.floor(Math.random() * unsei.length ); return unsei[nmb]; } // おみくじ export let omikuji = (): void => { let massage: string = 'あなたの運勢は「' + kekka() + '」です。'; alert(massage); }
「script.ts」のイベント以外を持ってくる。omikuji()
にはexport
を付ける。
default
インポートはimport側で自由に関数名を付けれる。しかしそれは混乱の元にもなるため、あまりお勧めしない方法のようだ。
※参考:TypeScriptでモジュールを作成する/インポートする (export, import)|まくろぐ
「script.ts」のコード
import { omikuji } from "./omikuji/omikuji"; // イベント実行 document.addEventListener("DOMContentLoaded", () => { let btn: HTMLButtonElement = document.querySelector('.inner__text-btn') as HTMLButtonElement; btn!.addEventListener('click', omikuji, false); }, false);
冒頭で「omikuji.ts」をインポート。関数名omikuji
を読み込む。
コンパイル結果…エラーになった。。
コンパイル後のscript.js
"use strict"; exports.__esModule = true; var omikuji_1 = require("./omikuji/omikuji"); // イベント実行 document.addEventListener("DOMContentLoaded", function () { var btn = document.querySelector('.inner__text-btn'); btn.addEventListener('click', omikuji_1.omikuji, false); }, false);
exports.__esModule
、これはなんだろう??- インポート部分は
import()
(ESモジュール)からrequire()
(CommonJS)に変換されている
TypeScriptは「omikuji.ts」のコード自体がインポートされるようではないようだ*3。
そして「omikuji」フォルダや「omikuji.js」もコンパイルはされていない。
なので当然「omikuji
関数が見つからない」になると思われる。
コンソールを見ると下記のエラー
Uncaught ReferenceError: exports is not defined
キャッチされていないReferenceError:エクスポートが定義されていません
ふむ、omikuji
以前に冒頭のexports.__esModule = true
部分がエラーになっている。
exports
で検索かけても他のファイルには出てこない名前なんだよなー。
「おみくじ」ボタンを押しても… アラートは出なくなった。。
最後に
今回はここまでとします!
※わかったこと:
- TypeScriptはJSコードをそのまま書いても動く(後方互換)
- JSからTypeScriptに移植するにあたり、型の付け方に詳しくなった
- モジュールのインポートはSass(SCSS)やEJSのようにコードごとインポートされるわけではなさそう
次回は、今回発生したエラーの検証と対策にトライします。
エラーが解消したらomikujiモジュールの中のパーツもさらにモジュール化したいです。
それではまた!
※参考:【メタ言語】HTMLテンプレートエンジン、AltCSS、AltJSのまとめ
qiita.com