$30 off During Our Annual Pro Sale. View Details »

TypeScriptってどんな言語? 言語そのものを知る面白さ

uhyo
November 08, 2023

TypeScriptってどんな言語? 言語そのものを知る面白さ

2023年11月8日
プロを目指す人のためのTypeScript入門 - Forkwell Library #35

uhyo

November 08, 2023
Tweet

More Decks by uhyo

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. 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);
    }

    View Slide

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

    View Slide

  22. 論理和を論理積で表現しない
    なぜ良くないのか? →設計上意図しない値が混ざるから
    // 意図した値
    { 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;
    }

    View Slide

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

    View Slide

  24. 論理和を論理積で表現しない
    論理和のほうが、論理積より正確に可能性を表現できる。
    // 論理和
    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通り(ある / 無い)

    View Slide

  25. 正確に可能性を表現する利点
    種類の判別方法を「タグを見る」に強制できるので、バグりにくい。
    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) { … }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  38. 技術の進化の例①
    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");
    }

    View Slide

  39. 技術の進化の例①
    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を式として使える提案なのか」
    🫐 文と式という概念を知っているため、どこが既存機能
    との差分なのか理解できる。
    用 語 を 知 っているためプロポーザル 名 から 内 容 を
    推測できる。

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    を担保した。

    View Slide

  44. 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/
    より引用

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  49. 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/
    より引用

    View Slide

  50. まとめ

    View Slide

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

    View Slide