プログラミングTypeScript 1章〜5章を読んで
こんにちは、matoです。
- 最近、本サービスであるzenn.devを利用してる方が多いなと感じ、試しにどんなものか書いてみたい。
- TypeScriptは、React Nativeでスマホアプリを開発する上で、最近書き始めたが、まとまった勉強なしでコードを書いていたので、TypeScript力を上げるべく本を読んだが、せっかくなのでなんらかアウトプットしたい
- 一冊370ページ全部読んでから記事を書こうと思ったが、途中で挫折したため、一旦5章までとした。
という想いと経緯で、本記事を書くに至りました。
本は、こちらです。(一応アフィリリンクです)
自分の考えるTypeScriptのざっくりメリット
JavaScriptの場合、実行時にエラーでツラいのですが、TypeScriptは型付になるのでトランスコンパイル時にエラーになり早く間違いに分かったり、静的解析でエディタ上でもエラーを表示させることができたり、型による自動補完がきいてコードをサクサク書けて最高の気分になります。
ここからは、本の中で改めてこれ便利だよな〜とか、これ知らなかった!というのをピックアップ形式で書いてきます。
リテラル型
// before
type FishName = string
let fishName: FishName = 'Dalmatian' // no error
// after
type FishName = 'Tuna' | 'Shark' | 'SeaPearch'
let fishName: FishName = 'Dalmatian'
// Type '"Dalmatian"' is not assignable to type 'FishName'.ts(2322)
ということで、エラーにしてくれます。
ちなみにこんな使い方も。
type IsFish = true
let isFish: IsFish = false
// Type 'false' is not assignable to type 'true'.ts(2322)
当たり前なのですが、booleanのvalueもリテラル型にできます。
インデックスシグネチャ
let aquarium: {
[fishName: string]: number
} = {
'NeonTetra': 10,
'CardinalTetra': 10,
'CorydorasPanda': 5,
'RummyNoseTetra': '10',
// Type 'string' is not assignable to type 'number'.ts(2322)
}
といった形で、[key: T]: Uというシグネチャを記載することで、型チェックをすることが可能です。
オブジェクトにreadonly修飾子
let neonTetra: {
readonly size: number
} = {
size: 3
}
neonTetra.size = 5
// Cannot assign to 'size' because it is a read-only property.ts(2540)
まだ使ったことなかったですが、今後はどしどし使っていこうと思います。
Typeの合併型と交差型
type Fish = {
canBranchialRespiration: boolean // えら呼吸
}
type Human = {
canPulmonaryRespiration: boolean // 肺呼吸
}
// 合併型
type FishOrHumanOrBoth = Fish | Human
let fishOrHumanOrBoth: FishOrHumanOrBoth = {
canBranchialRespiration: true,
}
// 交差型
type FishAndHuman = Fish & Human
let fishAndHuman: FishAndHuman = {
canBranchialRespiration: true,
// Property 'canPulmonaryRespiration' is missing in type '{ canBranchialRespiration: true; }' but required in type 'Human'.ts(2322)
}
合併型は和集合、交差型は積集合という表現があったので、てっきり交差型は2つの型の両方が重複したプロパティしかTypeを引き継げないと思ったのですが、そんなことはなかったのが、一つ驚きでした。
ぬるぽについて
古いバージョンのTypeScrip、またはコンパイラの設定で、strictNullChecksオプションをfalseの場合は、null許容してしまうらしい。
オプションは明示的なので分かるが、昔のTypeScriptがそうだったとは・・
(いつのVersionまでそうだったのかは確認してません)
関数コンストラクタ
let swim = new Function('fishName', 'return fishName + " swims."')
こんな関数宣言の方法もあるらしい。
ただし、これだとパラメータと戻り値に型情報を書けないのでもちろん非推奨。
call, apply, bind
JavaScriptととしても、怪しかった分野だが、ようやくちょっと理解した。
それぞれ、第一引数で、thisをbindしてくれる。3つの違いは、第2引数以降の渡し方と、関数自体を呼び出すのか新しい関数を返してくれるか。
ジェネリックはいつバインドされるか?
// <T>を呼び出しシグネチャの一部として呼び出したので、具体的な型にbindするのは関数を呼び出すとき
type Filter = {
<T>(array: T[], f: (item: T) => boolean): T[]
}
let filter: Filter = (array, f) => {
let result = []
for (let i = 0; i < array.length; i++) {
if (f(array[i])) result.push(array[i])
}
return result
}
// ココでbind
filter([1, 2, 3], _ => _ > 2)
// 型エイリアスに<T>を設定すると、bindするのは、filter宣言時
type Filter<T> = {
(array: T[], f: (item: T) => boolean): T[]
}
// ココでbind
let filter: Filter<number> = (array, f) => {
let result = []
for (let i = 0; i < array.length; i++) {
if (f(array[i])) result.push(array[i])
}
return result
}
filter([1, 2, 3], _ => _ > 2)
はい、面白いです。
クラスは構造的に型付けされる
class Human {
breath() {}
}
class Fish {
breath() {}
}
function breath(animal: Human) {
animal.breath()
}
let human = new Human
let fish = new Fish
human.breath()
fish.breath()
// エラーなし
C#, Javaなどではクラスは名前によって型付けされてので、人によっては驚きかと思います。私はエンジニア歴は浅いのでただただ「勉強になる」って感じ笑
ポリモーフィズム(多態性)
関数同様にクラスもインターフェースもジェネリック型パラメータをサポートしてるので、ポリモーフィズムが可能です。
5.8: ミックスイン
多重継承したいというモチベーションはわかるけど、コードは良くわからず飛ばした orz
また、一周したら戻ってくるかな。
5.9: デコレーター
これも、GoFのデザインパターンの本で学んでたので、概念はわかるけど、コードは良くわからず飛ばした orz
最後に
実際にアプリを作っていても、複雑な型エラーの場合に時間がかかることがあったが、多少は改善されると思う。
あと、インデックスシグネチャとか名前を色々知らなかったのや、そもそもtypeがaliasであったこととか、知らなかった挙動が知れて面白かった。
ここまでで、142/377ページ。残り235ページが楽しみながら読もう。
Discussion