環境構築
インブラウザ・コンパイル では TypeScript
が使えないので webpack
などのローダーを使う必要があります。
サクッと環境構築するために、公式で用意されているサンプルをベースにしました。
https://github.com/riot/examples/typescript
ダウンロード
https://github.com/riot/examples
[Clone or download] -> [Download ZIP]
すべてのexampleがダウンロードされるので、その中にあるtypescriptを使います。
インストール
cd ./path/to/riot-examples/typescript
npm install
起動
npm start
ブラウザで↓を開くと乱数を生成するサンプルが表示されます。
http://localhost:3000/
TypeScriptでのRiotコンポーネントの書き方
riot-example/typescript のコードを JavaScript
の記述と比べながら確認してみます。
random.riot のテンプレート部分
<random>
<h3>{ props.title }</h3>
<button onclick={ generate }>
Generate
</button>
<h1>
{ state.number }
</h1>
<logs logs={ state.logs } onclear={ clearLogs }></logs>
<script type="ts">
// ...(省略)
</script>
</random>
こちらは JavaScript
で書く方式と同じですね。
random.riot の Script 部分
<random>
<!-- (省略) -->
<script type="ts"> // (1)
import Logs from '../logs/logs.riot'
import {RandomComponent} from './types' // (2)
function Random(): RandomComponent { // (3)
return {
state: {
number: null,
logs: []
},
generate(event: MouseEvent): void { // (4)
this.update({
number: Math.floor(Math.random() * 10000),
logs: this.state.logs.concat({
text: `Generate button clicked. Event type is ${event.type}`
})
})
},
clearLogs(): void {
this.update({
logs: []
})
}
}
}
Random.components = {
Logs
}
export default Random
</script>
</random>
-
TypeScript であることを
type="ts"
で指定します。 -
後述する型定義ファイルをインポートしています。
-
一見ガラッと変わっているように見えますが、JavaScriptでもファクトリ関数を使ってコンポーネント生成ができるので同じです。
https://riot.js.org/ja/api/#ステートハンドリング -
引数と戻り値の型が指定できます。
random.riot と対になっている types.ts
import {RiotComponentExport} from 'riot'
export interface RandomComponentState { // (1)
number: number | null;
logs: { text: string }[];
}
export interface RandomComponentProps { // (1)
title: string;
}
export interface RandomComponent extends RiotComponentExport<RandomComponentProps, RandomComponentState> { // (2)
generate(event: MouseEvent): void;
clearLogs(): void;
state: RandomComponentState;
}
JavaScript
で記述する際には存在しない型定義ファイルで、コンポーネントのインターフェースが定義されています。
-
props
とstate
のフィールドとそれぞれの型が指定できます。 - メソッドの定義を指定できます。
TypeScript の恩恵はどこで受けられるの?
ここが一番のポイントだと思うので色々試してみました。
number
型で定義されている変数に文字列を入れる
function Random(): RandomComponent {
return {
state: {
number: 'riot.js', // <-- ここを変更
logs: []
},
// ...
}
}
// -------------------------------
// random.riot.ts
// Error 2322 : Type 'string' is not assignable to type 'number | null'.
アサインできない型なのでエラーになりました。
types.ts
にだけ関数を追加する
export interface RandomComponent extends RiotComponentExport<RandomComponentProps, RandomComponentState> {
generate(event: MouseEvent): void;
clearLogs(): void;
hoge(): void; // <-- ここに追加
state: RandomComponentState;
}
// -------------------------------
// random.riot.ts
// Error 2741 : Property 'hoge' is missing in type '{ state: { number: null; logs: never[]; }; generate(event: MouseEvent): void; clearLogs(): void; }' but required in type 'RandomComponent'.
インターフェースが定義されているのに実装が存在していないのでエラーになりました。
types.ts
の state
にだけフィールドを追加した場合も同様の結果でした。
random.riot
にだけ関数を追加する
function Random(): RandomComponent {
return {
// ...
clearLogs(): void {
this.update({
logs: []
})
},
fuga(): void { // <-- ここに追加
// 何らかの処理
}
}
}
これはエラーにはならなかったです。
テンプレート部分から呼び出す場合は型定義を書いて、ローカル関数は書かないという使い方ができそうです。
.riot
ファイルからコード補完は効くのか
Visual Studio Code では効きませんでした。
Riot.js が HTML の中にスクリプトを書く形式なので仕方がないですね。
感想
型定義ファイルを書く分作業量は増えてしまいますが、インターフェースの恩恵を受けたいケースでは使えそうに思いました。