Slide 1

Slide 1 text

TypeScriptってどんな言語? 言語そのものを知る面白さ Forkwell Library #35 『プロを目指す人のためのTypeScript入門』

Slide 2

Slide 2 text

講演者紹介 uhyo 『プロを目指す人のためのTypeScript入門』著者 株式会社バベル プリンシパルエンジニア TypeScriptとReactが得意。 最近ハマってる技術はGraphQL。

Slide 3

Slide 3 text

著書紹介 プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで 2022年4月発売 第4刷まで出てます!

Slide 4

Slide 4 text

本の内容は? TypeScriptという言語そのものにフォーカス して約400ページ。 フロントエンド・サーバーサイドといった 個別の領域のコンテンツはほぼ無し。 (演習用にNode.jsのAPIを使う場合もあり) 第1章 イントロダクション 第2章 基本的な文法・基本的な型 第3章 オブジェクトの基本とオブジェクトの型 第4章 TypeScriptの関数 第5章 TypeScriptのクラス 第6章 高度な型 第7章 TypeScriptのモジュールシステム 第8章 非同期処理 第9章 TypeScriptのコンパイラオプション

Slide 5

Slide 5 text

本のコンセプト 応用に踏み込まず、TypeScriptそのものを学ぶ 言語そのものの学習に絞っても400ページくらいの内容になる。 (これでもハイレベルな内容を削っている…… もっと高度な内容がいいという声もそこそこある) 書名に「プロを目指す人のための」とあるので、 プロフェッショナルにふさわしい盤石な知識をつけることを目指した内容。

Slide 6

Slide 6 text

本のコンセプト JavaScriptの経験がなくてもTypeScriptを学べる 本が企画された当時は、TSはJSをすでに知っている人がプラスアルファで学ぶ ものという風潮が結構あった。 この本では、TSをひとつの言語として取り扱い、JSをまだ知らなくてもTSを 学べるような構成にした。 🫐 ブルーベリー本七不思議① JSとTSの境界が分かりやすいという感想と分かりにくいという感想が両方あった

Slide 7

Slide 7 text

本のコンセプト 教科書的な内容 1ページ全部占めるような長いサンプルコードはあまり無い。 文章量が多い。 細かく分けられたサンプルコードと文章で一つ一つの要素を説明。 実際の応用は読者に任せ、この本ではとことん基礎固めを行う。 🫐 ブルーベリー本豆知識:あまりに応用が無さすぎるのを多少緩和するために「力試し」コーナーができた

Slide 8

Slide 8 text

本のコンセプト ごまかさない 説明はなるべく厳密に行い、仕組みを正しく説明する。 (分かりやすさのために緩い説明から始まることもあるが) 可能な限り、仕様書や公式ドキュメントに由来する正確な用語を使う。 理解しやすさのために正確性を犠牲にするのではなく、分かりやすい説明で 正確に理解してもらうことを目指す。

Slide 9

Slide 9 text

本の内容おさらいと TypeScriptの面白いところ

Slide 10

Slide 10 text

2章 基本的な文法・基本的な型 プログラムが上から下に実行されることや、変数宣言など基本的な構文を学ぶ。 条件分岐やループなどの基本的な制御構文や各種の演算子も。 型アノテーション等のTS特有の構文も同時に学ぶ。 型システムの面ではstringなどプリミティブ型の扱いを学ぶ。 🫐 一押しポイント: 文と式の違いや「式文」についても正確な説明で学ぶ。 プログラミング言語を「漠然とした文字の並び」ではなく 「体系立てられた構文」として理解する一助とするため。

Slide 11

Slide 11 text

3章 オブジェクトの基本とオブジェクトの型 オブジェクトの概念と、オブジェクトに関連した構文を学ぶ。 3章ではデータ構造としてのオブジェクトを扱い、 普通のオブジェクト( { } )と配列( [ ] )を学ぶ。 オブジェクトの型の表し方も一緒に学習する。 ジェネリック型 Foo もここで導入。 🫐 一押しポイント: オブジェクトに対する === の判定 や 部分型関係などの 込み入った話題も解説し、曖昧な点を後に残さない構成に。 (広く浅く進んでいくよりは、単元ごとにしっかり深入りする教科書っぽい構成を意識)

Slide 12

Slide 12 text

4章 TypeScriptの関数 関数の定義、呼び出し方、関数型の表し方などを学ぶ。 もちろんジェネリック関数も合わせて解説。 ついでにこの章ではスコープの概念も導入する。 🫐 一押しポイント: コラム19「型引数はどのように推論されるのか」では、ジェネリック関 数の型引数を省略した場合にどう推論されるのかを詳しく解説。 一見魔法のような型推論の挙動の裏側をしっかり解説し、深い理解を促す。

Slide 13

Slide 13 text

5章 TypeScriptのクラス クラスは便利なときはとても便利なので、1章を割いて解説。 また、例外処理周りを差し込むうまい場所が無かったので、 エラーオブジェクトは主にnew Errorで作るという理由で5章に間借りした。 🫐 一押しポイント: JS/TSの面倒なところとして知られるthisも妥協せずに解説。

Slide 14

Slide 14 text

6章 高度な型 応用的な型を学ぶ盛りだくさんな章で、一番長い章。 ユニオン型・インターセクション型・リテラル型・keyof型やanyとunknown、asなどを漏れ なく学習する。 TS特有の事情の説明が多くなってきて、本書の中でも特に面白いところ。 🫐 一押しポイント: タグ付きユニオンや型の絞り込みについても、TSの肝として力を入 れて解説。 😥 微妙なポイント: mapped typesなどの高度な型も簡単に触れているが、細かい説明 は読者のレベルを考慮して省いた。 (説明欲しかったという声も多少あり)

Slide 15

Slide 15 text

7章 TypeScriptのモジュールシステム 主にimport/export構文の使い方を学ぶ。 他にもTypeScript向けのモジュールシステムを代表して、Node.jsやnpm、 DefinitelyTypedといったエコシステムの仕様に触れる。 🫐 一押しポイント: モジュール関連の構文は漏れなく解説。 (dynamic importはPromiseが絡むので8章に持ち越し)

Slide 16

Slide 16 text

8章 非同期処理 TypeScriptの非同期処理について一通り解説し、しっかりした理解を目指す。 もちろん主なコンテンツはPromiseやasync/await。 🫐 一押しポイント: new PromiseでPromiseを作る方法や、Promiseチェーンの 仕組みなどPromiseの仕組みを細部までしっかり伝授。 (マイクロタスクなどの裏側の仕組みは、意味論との関わりが少ないと考え省略)

Slide 17

Slide 17 text

TypeScriptの面白いところは6章に集中 2章〜5章は基礎をちゃんとやる内容。 かなり紙面を割いているので、質の高い知識が身につくはず。 とはいえ6章の内容がやはり一番面白い。 🫐 すでにある程度TSを知っている人は6章から読めば良いという意見もあった

Slide 18

Slide 18 text

TypeScriptの面白いところ ユニオン型と型の絞り込みが面白い。 使うのと使わないのでは、設計の質に大きな差がある。 ユニオン型でTypeScriptのポテンシャルを引き出そう。 ユニオン型で処理の結果を表す例 type OperationResult = | { success: true; data: T } | { success: false; error: unknown } // 型に当てはまる値の例 { success: true, data: “pikachu” } { success: false, error: new Error(...) }

Slide 19

Slide 19 text

TypeScriptの面白いところ 他にもkeyof, mapped types, conditional types など面白い言語機能はいろいろ備わっているが、 本書ではさらっとしか扱っていません 🫐 とはいえ実務でもある程度使うので、扱ってもよかったかも ……

Slide 20

Slide 20 text

TypeScriptの面白いところ 特にTypeScriptはタグ付きユニオンのサポートが手厚い。 (「タグ」を見れば判別できるユニオン型をタグ付きユニオンと言う) type OperationResult = | { success: true; data: T } | { success: false; error: unknown } // この場合はsuccessプロパティがタグ if (result.success) { // success: true の場合に絞り込まれる console.log(result.data); } else { // success: false の場合に絞り込まれる console.error(result.error); }

Slide 21

Slide 21 text

論理和を論理積で表現しない ユニオン型を知らないと、次のように表現しがち。 type OperationResult = | { success: true; data: T } | { success: false; error: unknown } // よくない表現 type OperationResult = { success: boolean; data?: T; error?: unknown; }

Slide 22

Slide 22 text

論理和を論理積で表現しない なぜ良くないのか? →設計上意図しない値が混ざるから // 意図した値 { success: true, data: “pikachu” } { success: false, error: new Error(...) } // 意図していない値 { success: true, error: new Error(...) } { success: false, data: “pichu” } { success: true } { success: true, data: “pikachu”, error: new Error(...) } // よくない表現 type OperationResult = { success: boolean; data?: T; error?: unknown; }

Slide 23

Slide 23 text

論理和を論理積で表現しない 型で意図を正確に表現することはとても重要。 型(関数のインターフェースなども含む)は設計をコードで 直接表現したものである。 実際の運用と乖離したインターフェースはメンテナンス性の 問題がある。

Slide 24

Slide 24 text

論理和を論理積で表現しない 論理和のほうが、論理積より正確に可能性を表現できる。 // 論理和 type OperationResult = | { success: true; data: T } | { success: false; error: unknown } // 論理積 type OperationResult = { success: boolean; data?: T; error?: unknown; } 1 + 1 = 2 成功: 1通り 失敗: 1通り 2 × 2 × 2 = 8 success: 2通り(true / false) data: 2通り(ある / 無い) error: 2通り(ある / 無い)

Slide 25

Slide 25 text

正確に可能性を表現する利点 種類の判別方法を「タグを見る」に強制できるので、バグりにくい。 type OperationResult = | { success: true; data: T } | { success: false; error: unknown } // タグを見ないと絞り込めない! if (result.success) { console.log(result.data); } else { console.error(result.error); } type OperationResult = { success: boolean; data?: T; error?: unknown; } // いろいろなやり方ができてしまう…… if (result.success) { … } if (result.data) { … } if (!result.error) { … }

Slide 26

Slide 26 text

正確に可能性を表現する利点 種類の判別方法を「タグを見る」に強制できるので、バグりにくい。 これは正常系で data に number が入 る仕様だと0のときバグるぞ! type OperationResult = { success: boolean; data?: T; error?: unknown; } // いろいろなやり方ができてしまう…… if (result.success) { … } if (result.data) { … } if (!result.error) { … }

Slide 27

Slide 27 text

正確に可能性を表現する利点 種類の判別方法を「タグを見る」に強制できるので、バグりにくい。 try-catchで例外を OperationResultに変 換する場合、 throw undefinedされたら困るぞ! (普通はやらないけど) type OperationResult = { success: boolean; data?: T; error?: unknown; } // いろいろなやり方ができてしまう…… if (result.success) { … } if (result.data) { … } if (!result.error) { … }

Slide 28

Slide 28 text

正確に可能性を表現する利点 型として表現することで、設計意図を読み手に伝えられる。 さらに、静的解析の恩恵を受けられる。 6章で紹介した網羅性チェックなど。 ユニオン型という「または」を表すために適した道具を使うことで、 変なエッジケースの発生を避けることができる。

Slide 29

Slide 29 text

本を読んで基礎を固める理由を考える

Slide 30

Slide 30 text

なぜ基礎を固めるのか? 読者次第で幅広い応用が可能 基礎的な知識は、読者が自分で応用する必要がある代わりに、応用の幅が広い。 裾野を広く持って活躍したい人、将来性を重視する人におすすめ。 ● アプリケーションの分野・領域を問わず役立つ ● 別の言語の類似概念を学習する助けになる ● などなど……

Slide 31

Slide 31 text

なぜ基礎を固めるのか? 自ら考え、自ら調べる助けとなる プログラミングはやり方を暗記するだけでなく、自ら考察したり、 分からないことを調べたりする必要がある。 正しい考察には正しい前提知識が必要。 前提知識があると、「何が分からないのか分からない」ことが減る。 (これは最近はAIさんに聞けば大体なんとかなるが)

Slide 32

Slide 32 text

なぜ基礎を固めるのか? 自信を持ってコードが書けるようになる プロとして、自分が書いたコードの意味を説明できなければいけない。 そうでなければ「よく分からないけど動く」状態になってしまう。 自分が使っているプログラミング言語の動作機序を正確に理解していなければ、コード の意味を説明できない。 🫐 関連仕様を読みに行けるのが理想。 🫐 非同期処理を扱う場合どこで競合が発生しうるのかコードを見て正しく判断できるとか

Slide 33

Slide 33 text

例: クイズ // data: string const prefix = data.slice(0, prefixLength); これまでprefixLengthがany型だったため型エラーが起きていませんでしたが、 prefixLengthの型を直したら実はstring型だったため型エラーが起きました。 しかし何かランタイムでは動いているようでした。 動作を変えずに型エラーを消すために、 data.slice(0, Number(prefixLength)) と変えても良いでしょうか?

Slide 34

Slide 34 text

例: クイズ // data: string const prefix = data.slice(0, prefixLength); const prefix = data.slice(0, Number(prefixLength)) 答え: 変えていい JavaScriptの仕様書を参照すると、String.prototype.sliceに文字列が渡された場合に はToNumberで数値に変換されることになっている。 一方、自分でNumberを呼び出すのも結局内部的にはToNumberに行き着くため、どち らでも同じ処理になるため動作が変わらない。

Slide 35

Slide 35 text

例: クイズ // data: string const prefix = data.slice(0, prefixLength); const prefix = data.slice(0, Number(prefixLength)) 仕様書が読めれば、このような変更を、根拠を持って行うことができる。 🫐 この本だけでは仕様を読みに行けるところまでは到達しないが、仕様書の紹介はしているし基礎固めにな   るはず……

Slide 36

Slide 36 text

なぜ基礎を固めるのか? 何より面白い! 実際に動くものを作れるようになるのも面白いが、 仕組みを理解するのって面白いと思いませんか? よく考えられた言語の仕組みに触れることは、面白さと実益を兼ね備える。 🫐 技術を面白いと思う人結局強いみたいな説もあるため 🫐 とくにTypeScriptはAnders御大のセンスが光る

Slide 37

Slide 37 text

正確な理解で技術の進化に立ち向かう プログラミング言語は日々進化している。 JavaScript / TypeScriptも例外ではない。 技術の進化を効率よく理解するために、 既存の部分をしっかり押さえておく必要がある。 あと、新しい技術をスムーズに理解できると面白いので、 モチベーションやキャッチアップ力に良い影響を与える。

Slide 38

Slide 38 text

技術の進化の例① Stage 2 Proposal: throw expression https://github.com/tc39/proposal-throw-expressions 最近rbucktonさん(TypeScriptチーム)によって進められているプロポーザル。 🫐 プロポーザル: JavaScriptの新機能。仕様がまとまるとステージが上がり、 3で内定、4で正式採用となる コード例(引用): function getEncoder(encoding) { const encoder = encoding === "utf8" ? new UTF8Encoder() : encoding === "utf16le" ? new UTF16Encoder(false) : encoding === "utf16be" ? new UTF16Encoder(true) : throw new Error("Unsupported encoding"); }

Slide 39

Slide 39 text

技術の進化の例① function getEncoder(encoding) { const encoder = encoding === "utf8" ? new UTF8Encoder() : encoding === "utf16le" ? new UTF16Encoder(false) : encoding === "utf16be" ? new UTF16Encoder(true) : throw new Error("Unsupported encoding"); } 基礎知識が薄い人の反応例 「throwってエラー発生させるやつだよね? こ れは何が新しいの?  でも何か違和感があるような」 基礎知識がしっかりした人の反応例 「なるほど、既存のthrowは文だけどこれは  新しくthrowを式として使える提案なのか」 🫐 文と式という概念を知っているため、どこが既存機能 との差分なのか理解できる。 用 語 を 知 っているためプロポーザル 名 から 内 容 を 推測できる。

Slide 40

Slide 40 text

技術の進化の例② Stage 3 Proposal: Promise.withResolvers https://github.com/tc39/proposal-promise-with-resolvers 何かすごい勢いでStage 3まで駆け上がった便利関数のプロポーザル。 コード例(引用): const { promise, resolve, reject } = Promise.withResolvers();

Slide 41

Slide 41 text

技術の進化の例② const { promise, resolve, reject } = Promise.withResolvers(); 基礎知識が薄い人の反応例 「あーPromiseは非同期処理のやつね。  もちろん知ってるけどpromiseと一緒に 返さ れてるresolveとrejectって何?」 基礎知識がしっかりした人の反応例 「Promiseの静的メソッドってことはPromiseを  新しく作る関数だな。resolveとrejectは  new Promiseでコールバックに渡される関数を 取り 出せる感じかな?」 🫐 Promiseの動作機序やnew Promiseのシグネチャを   基礎知識として知っており活用できるため、   Promise.withResolversの機能・利便性も   想像できる。

Slide 42

Slide 42 text

ちなみにTypeScriptはどう進化したのか? 本の発売から現在までの主な変化をご紹介します

Slide 43

Slide 43 text

発売当時のバージョンと今のバージョン 発売当時のバージョン: TypeScript 4.6 (2022年2月リリース) 現在のバージョン:   TypeScript 5.3(2023年8月リリース) 🫐 裏話: 本が4月発売なので、同年2月(しかも28日)リリースのバージョンに対応するのはけっこう粘っている。 スナップショットテスト的な仕組みを用意して本に登場するコード片の結果が最新の TypeScriptバージョンでも変わらないこ と を担保した。

Slide 44

Slide 44 text

TypeScript 4.7の主な変化 .ctsと.mtsの導入、mode: “node16” の追加など、 Node.jsのCJS/ESM両対応機能にTypeScript側も対応した。 🫐 本ではNode.jsでサンプルコードを動かすから正直これは発売前に欲しかった 型引数に変性パラメータ(in, out)を書ける機能が追加された。 🫐 けっこう難しい機能だが、本書を読んで   共変・反変について理解していれば大丈夫。   これもあったほうが説明が楽だったかも type Getter = () => T; type Setter = (value: T) => void; interface State { get: () => T; set: (value: T) => void; } https://devblogs.microsoft.com/typescript/announcing-ty pescript-4-7/ より引用

Slide 45

Slide 45 text

TypeScript 4.8の主な変化 unknown と {} | null | undefined の相互運用性が強化された。 事実上 {} は「nullとundefined以外全部」という意味の型だったが、 その性質がより明確になり、さらに一貫性が増して嬉しい。 // something: unknown if (something !== null) { // something: {} | undefined } 🫐 これもあったほうが説明しやすいので欲しかった(毎回言ってる)

Slide 46

Slide 46 text

TypeScript 4.9の主な変化 satisfies構文が追加された。 地味に効能が分かりにくい構文だが、基本用語や型推論の機序を知っていれば 理解しやすい! 式 satisfies 型 (式 satisfies 型)の型は式の型推論結果と変わらない。 ただし、式の型推論時にContextual Typeとして型が用いられる。 また、式の型推論結果が型の部分型であることもチェックされる。

Slide 47

Slide 47 text

TypeScript 5.0の主な変化 デコレータ(Stage 3版)がサポートされた。 クラスの表現力を向上させる機能。厚い本がさらに厚くなる。 const型パラメータもサポートされた。 毎日使うものではないがライブラリ等を書く人には嬉しい機能。

Slide 48

Slide 48 text

TypeScript 5.1の主な変化 細かい改善はあるし、例えばReactのasyncコンポーネントの型定義が書けるようになっ たのはTS 5.1からだが、 機能的に目立つ変化ではない印象。

Slide 49

Slide 49 text

TypeScript 5.2の主な変化 using宣言のサポートが大きい。 var, let, constに次ぐ第四の変数宣言である。 🫐 解説を本に入れるとしたらどの章に入れるかすごく迷いそう 右のコードは、関数を出たら 一時ファイルが自動的に削除 される。 try-finallyの発展と考えると 理解しやすい。 export function doSomeWork() { using file = new TempFile(".some_temp_file"); // use file... if (someCondition()) { // do some more work... return; } } https://devblogs.microsoft.com/typescript/announcing-typescript-5-2/ より引用

Slide 50

Slide 50 text

まとめ

Slide 51

Slide 51 text

まとめ 本の内容とコンセプトを紹介しました。 TypeScriptの面白いところとしてユニオン型周りのトピックを紹介しました。 さらに、本で基礎を固めるメリットをいくつか紹介して、 特に技術の進化について行く足がかりとなることを説明 しました。 ついでに、TypeScript自体の最近の進化についても 説明しました。