Slide 1

Slide 1 text

Frontend Boot Camp 2023-08-23 #hatenaintern)*)+

Slide 2

Slide 2 text

このブートキャンプのゴール • 皆さんが主に後半のチーム開発でフロントエンドに関わるようなタスクに取り組む際 に困らない最低限の知識をつけてもらう • この講義で扱う"フロントエンド"の領域は JavaScript/TypeScript/React • Web 開発において年々重要度を増しているフロントエンドを⾼速にキャッチアップ してもらう • 講義のボリュームが⼤きいこともあり、周辺技術に関する説明を省略する場合もあ る インターンが終わった後も React や TypeScript など、フロントエンド技術について知る 際の⼿助けになれば良いと思っています。 #hatenaintern)*)+

Slide 3

Slide 3 text

現代のフロントエンドについて (/0min) #hatenaintern)*)+

Slide 4

Slide 4 text

現代のフロントエンドに ついて 例えば、React*製のアプリケーションをつ くって公開するケースを考えてみましょう。 レイアウトを組むことはもちろん、アプリ ケーションが表⽰されるまでの流れを考え てみると必要とされる知識も多くなってき ます。 Frontend Developer Roadmap: Learn to become a modern frontend developer ! React ‒ ユーザインターフェース構築のための JavaScript ライブラリ #hatenaintern)*)+

Slide 5

Slide 5 text

様々な動作環境の⼀例 それぞれの環境には、これらの技術によって備えることができます。 • 古いスマホやパソコン(IE や古いバージョンのブラウザ) • Web 標準 • アクセシビリティ機能を使っているユーザ • アクセシビリティ (accessibility, a88y) • (利⽤制限がかかっている)遅い‧不安定なインターネット • パフォーマンス対策 それぞれ、どのように対策するのか⾒ていきます。 #hatenaintern)*)+

Slide 6

Slide 6 text

例: React アプリをユーザに表 ⽰するには? 1. (開発者)実装 • TypeScript の型チェック;、最新の JavaScript 構⽂での開発 2. (開発者)ビルド • Web 標準 • パフォーマンス対策 3. (ユーザー)Web ページへのリクエスト • html を読み込み、それに付随するリソースを読み込むf 4. (ユーザー)ページを表⽰ • 読み込んだリソースを元にページを表⽰する • アクセシビリティ (accessibility, aAAy) ! 読み込むファイルが⼤きい‧多いほど、次の段階であるページ表⽰が遅くなる ! なぜ TypeScript を使うのか? - TypeScript Deep Dive ⽇本語版 #hatenaintern)*)+

Slide 7

Slide 7 text

例: React アプリをユーザに表 ⽰するには? • Web 標準 • パフォーマンス対策 • アクセシビリティ (accessibility, aAAy) 具体的にはどうのように対策していくの か、開発の流れと共に⾒ていきます。 #hatenaintern)*)+

Slide 8

Slide 8 text

例: React アプリをユーザに表⽰するには? React を JavaScript のアプリケーションと考えるならば、素朴に html と⼀緒にアプリのソースコードを配信してブラウザで表⽰で きるはずです。 > ls index.html main.js #hatenaintern)*)+

Slide 9

Slide 9 text

例: React アプリをユーザに表⽰するには? しかし、React/TypeScript で開発していた場合はどうでしょうか?
// main.tsx const App = () => { const user: User | null = useUser() return (
Α͏ͦ͜ɺ{user?.name ?? 'ήετ'}͞Μɻ
) } const container = document.getElementById('root') const root = createRoot(container!) root.render() #hatenaintern)*)+

Slide 10

Slide 10 text

例: React アプリをユーザに表⽰するには? • TypeScript をブラウザが解釈できない • JavaScript なら const user = useUser() • TypeScript なら const user: User | null = useUser() • JSX をブラウザが解釈できない • JSX とは const label = () => (
...
) のように、 HTML タグを書いてあるものN • 新しい構⽂を解釈できないような、(未アップデートの)古いブラウザが存在する • ESabab で追加された構⽂ ?. h や ?? jなど => JSX や TypeScript、新しい JavaScript 構⽂をトランスパイル@によってブラウザが解釈できるように書き換える。 ! 未対応の構⽂をターゲットが解釈可能な構⽂へ書き換えることによって、その環境でもコードが動くようにすること。 ! Null 合体演算⼦ (??) - JavaScript | MDN ! オプショナルチェーン (?.) - JavaScript | MDN ! JSX の導⼊ ‒ React #hatenaintern)*)+

Slide 11

Slide 11 text

トランスパイル 最新の⽂法で書いたコードを多くの古い環境(古いブラウザなど)でも 動くように古い記法へ変換すること。 例えば、アロー関数式とテンプレートリテラル、Null 合体演算⼦ (??)を 使った関数があります。 const hello = (name) => `hello, ${name ?? "ήετ"}!` hello("hatena") // > hello, hatena! hello() // > hello, ήετ! #hatenaintern)*)+

Slide 12

Slide 12 text

トランスパイル アロー関数式とテンプレートリテラルは ES4567(ES9) から、Null 合体演 算⼦ (??)は ES4545 から導⼊されたもので、それ以降アップデートしてい ないブラウザでは動きません。 そのため、トランスパイルによって次のように変換します。 var hello = function hello(name) { return "hello, " + (name !== null && name !== void 0 ? name : "ήετ") + "!" } hello("hatena") // > hello, hatena! hello() // > hello, ήετ! #hatenaintern)*)+

Slide 13

Slide 13 text

ツールチェイン( • 前スライドで説明したトランスパイルを⾏うツールはトランスパイラと呼ばれ、代表的なものは 3 つあります。 • バンドラはトランスパイルしたモジュール群をできるだけ最適にバンドルしてくれるツールです。 • 最近はこれらのツールを結合する統合ツールのようなもの (Romec, Denog) が登場したりもしています。 トランスパイラ • Babel (JavaScript) • SWC (Rust) • esbuild (Golang) バンドラ • Webpack • Rollup • Parcel (SWC) • Snowpack, Vite (esbuild) ! https://deno.land/ ! https://rome.tools #hatenaintern)*)+

Slide 14

Slide 14 text

Web 標準 Chrome, IE, Safari, Firefox など世の中には多くのブラウザがあり、そのバージョンもユーザーによって 異なっています。 • Web 標準()という活動によって、すべてのブラウザで構⽂や API が同じ振る舞いをすることを⽬指して いる(( • JavaScript(ECMAScript)、HTML、DOM API、CSS の対応バージョンもブラウザによって異なる • もし古いブラウザでの動作をサポートするなら? • API が⾜りない場合は polyfill を⽤いて補う • 新しい JavaScript トランスパイルによって古い構⽂に変換する !! 2021 年のウェブ標準とブラウザ | gihyo.jp !" Web standards (ウェブ標準) - MDN Web Docs ⽤語集: ウェブ関連⽤語の定義 | MDN #hatenaintern)*)+

Slide 15

Slide 15 text

Web 標準 ブラウザごとのサポート状況は以下のサイトで確認できます。 • MDN%& • https://developer.mozilla.org/ja/docs/Web • ウェブ上には有象無象のあらゆる情報が転がっているので、フロントエンドやウェブ についてのことで困ったらまずは MDN を⾒るのがオススメ • Can I use • https://caniuse.com !" mozilla のドメイン下にあるが、Google やマイクロソフトなどが Open Web Docs という団体を通じて⽀援をしている https://opencollective.com/open-web-docs/updates/ introducing-open-web-docs #hatenaintern)*)+

Slide 16

Slide 16 text

アクセシビリティ (accessibility, a55y) アクセシビリティ | MDN ウェブ開発におけるアクセシビリティとは、何らかの理由により能⼒に制約がある場合でも、可能な限り多く の⼈々がウェブサイトを使⽤できるようにすることを意味します。アクセシビリティとは、個⼈の⾝体的‧認 知的能⼒やウェブへのアクセス⽅法に関わらず、可能な限りアクセス可能なコンテンツを開発することです。 W"C - Accessibility 「ハードウェア、ソフトウェア、⾔語、⽂化、所在地、物理的/精神的能⼒にかかわらず、ウェブは基本的に すべての⼈に向けて設計されています。ウェブがこの⽬的を達成できると、さまざまな聴⼒、視⼒、認知能⼒ をもつ⼈々がウェブにアクセスできるようになります。」 #hatenaintern)*)+

Slide 17

Slide 17 text

アクセシビリティ (accessibility, a55y) • セマンティックマークアップ,- (意味的 HTML) • リストには
      を使うなど • これによって、スクリーンリーダー などが適切に解釈できるようになる。 • 代替テキスト,V • ネットワークエラーなどで画像の読み込みに失敗した際は、alt にいれた⽂字を表⽰するなど • キーボード操作のサポート • キーボード操作 (Tab キーでのフォーカス移動など) のサポートなど !" HTML の画像 - ウェブ開発を学ぶ | MDN !" Semantics (セマンティクス) - MDN Web Docs ⽤語集: ウェブ関連⽤語の定義 | MDN #hatenaintern)*)+

Slide 18

Slide 18 text

パフォーマンス対策 (利⽤制限がかかっている)遅い‧不安定なインターネットを使っているユーザーが 主に対象となる。 • ユーザーの転送量や計算資源を浪費させるケース • 無駄な js や css、画像などのアセットのダウンロード • アプリケーションを 1 つにバンドルした巨⼤なファイル • 遅れて表⽰された要素によってページのレイアウトが変わってしまうkl => 最初のページに必要なものだけダウンロードして素早く表⽰するべき !" Cumulative Layout Shift (CLS) https://web.dev/i!=n/ja/cls/ #hatenaintern)*)+

Slide 19

Slide 19 text

パフォーマンス対策 (利⽤制限がかかっている)遅い‧不安定なインターネットを使っているユーザーが主に対象とな る。 • 世界中のユーザーからのリクエスト • 例:⽇本にあるサーバーより前段、よりユーザーに近い位置(CDN, Edge)でリクエストを処理する • 複数回訪れる可能性のあるアプリケーション • CDN やブラウザキャッシュを⽤いて、何度も同じファイルをダウンロードする必要性を抑える • 1 度に巨⼤なファイルのダウンロードによって、ページの表⽰速度が遅い • コンテンツの表⽰タイミングにあわせてリソースを取得する • モジュール分割 (Code Splitting)、事前‧遅延読み込み (preload, defer, lazy) #hatenaintern)*)+

Slide 20

Slide 20 text

例: React アプリをユーザに表 ⽰するには? 1. (開発者)実装 • TypeScript の型チェック、最新の JavaScript 構⽂での開発 2. (開発者)ビルド • 古い環境をサポートするための、Transpile, Polyfill • ページごとに必要最低限の Bundle, Code Splitting 3. (ユーザー)Web ページへのリクエスト • html を読み込み、それに付随するリソースを読み込む 4. (ユーザー)ページを表⽰ • 読み込んだリソースを元にページを表⽰する • スクリーンリーダーやキーボード操作に対応する #hatenaintern)*)+

Slide 21

Slide 21 text

JavaScript について(01min) #hatenaintern)*)+

Slide 22

Slide 22 text

JavaScript について(01min) • ES$%&'(ES))以降に JavaScript に⼊った機能を中⼼に皆さんが 実際にコードを読む際に知っておいて欲しい JavaScript 上の概 念について紹介します • JavaScript とは · JavaScript Primer #jsprimer #hatenaintern)*)+

Slide 23

Slide 23 text

きほんのき 1/2 // ม਺ͱએݴ let a = "a" // let ͸্ॻ͖Մೳ const b = "b" // ্ॻ͖ෆՄೳ a = "A" // OK b = "B" // Cannot assign to "b" because it is a constant JavaScript における変数宣⾔は基本的に const を優先して使うこと を推奨します。変数が変更されないことがわかっている場合、コード を読む際の理解が容易になります。 #hatenaintern)*)+

Slide 24

Slide 24 text

きほんのき 1/2 const id = "1234" // string const name = null // null const age = 2022 // number const isAdmin = false // boolean // object const user = { id, username: name age, isAdmin, memo, } user.age // 2022 // ະఆٛϓϩύςΟͩͱ undefined ͕ฦΔ user.abc #hatenaintern)*)+

Slide 25

Slide 25 text

きほんのき 2/2 // ؔ਺ function getUser(id) { return db.users.find(id) } const getBlog = (id) => { return db.blogs.find(id) } // ഑ྻ const array1 = [1, 2] // ഑ྻॲཧ for (elm of array1) { console.log(elm) } array2.forEach((elm) => { console.log(elm) }) array1.map((elm) => elm * 2) // [2, 4] array1.at(0) // 1 #hatenaintern)*)+

Slide 26

Slide 26 text

Arrow Function • function や return を使わずに関数を⽣成できる • 基本的にはこの⽅法で関数を⽣成しておけばハマらないBC const hello = name => `Hello, ${name}` hello("world") // hello, world • 引数が 1 つのときは引数を囲う () も省略可 • Tips: 返り値がオブジェクトのときは () で囲う必要がある const getProps = () => ({ a: "foo", b: "bar" }) !" 主に this が関わってくるとハマるようになる。詳細は付録「Arrow Function について」を読んでください。 #hatenaintern)*)+

Slide 27

Slide 27 text

Arrow Function • 引数が複数ある場合は () で囲う • 関数内の記述が複数⾏に渡るときは {} と return を⽤いる const hello = (name, lang) => { if (lang === "ja") return `͜Μʹͪ͸ɺ${name}` if (lang === "es") return `Hola, ${name}` return `Hello, ${name}` } #hatenaintern)*)+

Slide 28

Slide 28 text

Promiseについて • ⾮同期処理を抽象化したオブジェクト • Pending(成功も失敗もしていない状態)な状態を 表現できる • 成功(resolve)すれば Fulfilled、失敗(reject) すれば Rejected という状態になります • Fulfilled か Rejected のどちらかになる(つ まり処理が完了した)状態を Settled と呼びま す。 • 成功すれば then メソッド、失敗すれば catch メソッド、それぞれのコールバック関数に処 理が⾮同期に引き継がれる #hatenaintern)*)+

Slide 29

Slide 29 text

const sleep = (ms) => new Promise((resolve, reject) => { setTimeout(() => { if (math.random() > 0.5) { return reject(new Error("error")) } else { return resolve(ms) } }, ms) }) sleep(1000) .then((ms /* resolveͨ͠஋Λड͚औΕΔ */) => console.log(`sleep: ${ms}ms`)) .catch((e) => { console.error(e) }) #hatenaintern)*)+

Slide 30

Slide 30 text

fetch を使った例 • fetch は HTTP 通信をするための API で Promise を返す • Promise は⾮同期なので、① が console に出⼒された後に成功/失敗それぞれの場合のメッセージが表⽰される /* Ϣʔβʔ৘ใΛऔಘ͢ΔAPIͷྫɻҎԼͷΑ͏ͳJSON͕ฦͬͯ͘Δͱ͍͏૝ఆɻ { user: {id: 1, name: 'hatena'} } */ const promise = fetch("https://api.example.com/user/1") .then((res) => res.json()) .then((json) => { const user = json.user console.log(`${user.name}ͷid͸${user.id}`) }) .catch(console.error) console.log(promise) // Promise {} ɾɾɾ ᶃ #hatenaintern)*)+

Slide 31

Slide 31 text

複数の Promise を扱う⽅法 Promise の配列を作成し、それを Promise.all() に渡すことで、複数の⾮同期処理を待ち合わせる。 const promises = urls.map((url) => fetch(url)) // ෳ਺ͷAPIͳͲΛฒྻʹୟ͘ Promise.all(promises) .then((responses) => { // ͢΂ͯͷ Promise ͕੒ޭͨ͠৔߹͜͜ʹདྷΔ }) .catch(console.error) • Promise.all() • 全てが成功すれば、Resolve(全ての結果の配列)を得る。1 つでも失敗すると Reject になる。 • Promise.allSettled() • 全てが完了した後に Resolve する。基本的には Reject しない。ここの Promise の実⾏結果を配列で得る ことが出来る。 #hatenaintern)*)+

Slide 32

Slide 32 text

async/await • async function の中であれば、await を⽤いて Promise を返す ⾮同期関数を同期関数のように扱えるIJ • エラーハンドリングする必要がある場合は try {} catch (e) {} を使⽤する !" 最近の ECMAScript の仕様に⼊っている Top-level await に対応している環境であれば async function の中で無くても await を利⽤できます #hatenaintern)*)+

Slide 33

Slide 33 text

async/await const getUser = async (id) => { try { const res = await fetch(`https://api.example.com/user/${id}`) const json = await res.json() const user = json.user console.log(`${user.name}ͷid͸${user.id}`) return user } catch (e) { console.error(e) throw new Error("error reason: *********") } } #hatenaintern)*)+

Slide 34

Slide 34 text

async/await • async function ⾃体も Promise を返す • Promise が返ってくる関数として、then を呼び出すことも出来る • つまり、呼び出す側では Promise が返ってくる関数であるという ことさえ分かっていれば、await で扱うことができる getUser(1) .then(console.log) // {id: 1, name: 'hatena'} .catch(console.error) #hatenaintern)*)+

Slide 35

Slide 35 text

class 宣⾔ • ES$%&' で class キーワードが追加された&4 • 積極的に使うことはそんなに無いかもしれないけど、読むことは多いかもしれない class Rect { constructor(width, height) { this.width = width this.height = height } // getter΍setterΛఆٛͰ͖Δ get area() { return this.getArea() } getArea() { return this.width * this.height } } const rect = new Rect(5, 10) console.log(rect.area) // 50 !" それ以前では function を使って、コンストラクタ関数を作って、プロトタイプ継承を利⽤してクラスを作っていた。class も単なるシンタックスシュガーなので、実体はコンストラクタ関 数です。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritanceandtheprototypechain #hatenaintern)*)+

Slide 36

Slide 36 text

ECMAScript Modules • プログラムをモジュールという単位に分割する機能 • 1 ファイル == 1 モジュール • スコープはモジュールごと • 関数や変数などをお互いに import/export できる • 歴史的経緯で CommonJS_`という形式が Node.js などを中⼼に⻑く採⽤されてきたが、 ECMAScriptxyで標準化されたモジュールの仕組みが今では Node.js、ウェブブラウザなどでも広 くサポートされているx_ !" 実際にはサポートしていないウェブブラウザやパフォーマンスのために Babel を使って変換した後に、モジュールを解釈して 1 つ〜複数のファイルをまとめて配信するために webpack な どを利⽤することが多い。詳細は付録「JavaScript をウェブアプリケーションで提供する際に使⽤するツールチェインについて」を参照してください。 !" ECMAScript は JavaScript の仕様における名称。ECMAScript 仕様については付録「JavaScript やフロントエンド周辺技術の標準(化)について」で解説しています。 !" module.exports という特殊なオブジェクトを経由してエクスポートし、require 関数を利⽤してインポートする⽅式。https://nodejs.org/docs/latest/api/modules.html #hatenaintern)*)+

Slide 37

Slide 37 text

default export // module.js export default function doSomething() {} • export default はモジュールから 1 つだけエクスポートできる • export default でエクスポートすると、import 時には任意の名 前を設定できる import awesome from "./module" #hatenaintern)*)+

Slide 38

Slide 38 text

名前付きエクスポート(named export) • 変数や関数に export を付加することで名前付きエクスポートが出来る • default export と異なり、いくつでも export できる // namedModule.js export const logType = { error: 'error', log: 'log' }; export const log = (message, type) => { if (type === 'error') return console.error(message); return console.log(message); } export const hello => log('hello'); #hatenaintern)*)+

Slide 39

Slide 39 text

インポートの⽅法について • export されている名前で取り込む import { log, hello } from "./namedModule" • as でリネーム出来る import { logType as LOGTYPE } from "./namedModule" • * as で export されているもの全てをオブジェクトにする;; import * as Logger from "./namedModule" Logger.hello() // 'hello' !! 必要なものだけを取り込むことで受けられる恩恵(webpack による TreeShakingなど)も多いので、基本的には * as は避ける⽅のがオススメ。 #hatenaintern)*)+

Slide 40

Slide 40 text

Tips #hatenaintern)*)+

Slide 41

Slide 41 text

for...of • 反復可能(iterable)なオブジェクトの要素を順番に取り出せる • 配列、⽂字列、NodeList、Map、Setなど const iterable = [10, 20, 30] for (const value of iterable) { console.log(value) } #hatenaintern)*)+

Slide 42

Slide 42 text

Nullish coalescing operator ?? • JavaScript では短絡評価のために || を⽤いる:;が、次のようなときに意図通りに動かない const rect = (width: number, height?: number) => ({ width: width, height: height || 100, // ߴ͞Λলུͨ͠ͱ͖͸σϑΥϧτ஋Λ༩͑Δ }) rect(50, 0) // {width: 50, height: 100} • 0 や ''(空⽂字)は Falsy な値12なので、短絡評価されてしまう結果、意図的に height に 0 を与えることが出来ない実装になっている。 • ?? は左辺が null または undefined のときだけ、右辺を返す演算⼦になっているので、このようなときは ?? を代わりに⽤いることで問題を 回避できる。 • 基本的には ?? を使っておくのがオススメ !" !!0 や !!'' のように boolean に変換した時に false となる値。JavaScript ではこの他に false と null と undefined と NaN などがある。Falsy でない値は boolean に変換した時に true とな る。こちらは Truthy な値と呼ぶ。 https://developer.mozilla.org/en-US/docs/Glossary/Falsy !" JavaScript では || は左辺が Truthy!6なら左辺の値を、左辺が Falsy なら右辺の値を返す。 && はその逆で左辺が Falsy なら左辺を、左辺が Truthy なら右辺を返すが、短絡評価に使われる ことは少ない。 #hatenaintern)*)+

Slide 43

Slide 43 text

Optional chaining ?. • オブジェクトが null や undefined な可能性があるとき、そのオブジェクトのプロパティを呼ぶために if による分岐や && によ る短絡評価HIを書く必要があった。 const val = nullOrObject && nullOrObject.method() const result = val && val.methodOfReturnValue() • ?. を⽤いると、呼び出し元が null または undefined のときはそれ以降のプロパティアクセスをしない。また、その場合は undefined として評価される。 const result = nullOrObject?.method()?.methodOfReturnValue() nullOrObject も nullOrObject.method() の返り値が共に null でも undefined でもないときに、 nullOrObject.method().methodOfReturnValue() の結果を得られる。 !" && は左辺が Falsy なときは右辺を評価しないことを利⽤して、null や undefined のときは右辺を評価しないように出来る。また、&& による短絡評価は便利なものの || と同様に Falsy に関 わる問題を時々引き起こしてしまうことに注意。この問題も Optional chaining で同時に回避できる。 #hatenaintern)*)+

Slide 44

Slide 44 text

Spread Syntax • ... を使うと配列やオブジェクトを展開出来る const sum = (a, b, c, d) => a + b + c + d const nums = [1, 2] const copied = [...nums] // த਎Λෳ੡ͨ͠഑ྻΛ࡞ΕΔ const moreNums = [...copied, 5] // [1, 2, 5] sum(...nums, 10) // 18 const obj = { a: 10, b: "foo" } const obj2 = { b: "bar", c: true } // 2ͭҎ্ͷobjectΛmerge͢ΔɻΩʔ͕ॏෳ͍ͯ͠Δ৔߹͸ޙʹॻ͍ͨํͰ্ॻ͖͞ΕΔ const merged = { ...obj, ...obj2 } // {a: 10, b: 'bar', c: true} const getUserConfig = (received) => ({ force: false, allowJs: false, ...received, // σϑΥϧτ஋Λ౉͞Εͨ஋͕͋Ε͹্ॻ͖͢Δ }) #hatenaintern)*)+

Slide 45

Slide 45 text

Rest Parameters • 関数の引数も ... で受け取ることで可変な⻑さの引数を受け取れる • Spread Syntax と違って、1 つだけ且つ引数の末尾でしか使えな い // const sum = (num1, num2, ...nums) => num1 + num2 + nums.reduce((a, b) => a + b, 0) const numbers = [1, 2, 3] sum(3, ...numbers, 6) // 15 #hatenaintern)*)+

Slide 46

Slide 46 text

TypeScript について(01min) #hatenaintern)*)+

Slide 47

Slide 47 text

TypeScript is 何 JavaScript に型アノテーションなどを付けられるようにしたプログラミング⾔語で、多くの場合は JavaScript に変換してから JavaScript の処理系で実⾏する。 • node.js や React の登場などで、サーバサイドやフロントエンドを横断して JavaScript を使⽤したそこそ こ⼤きなウェブアプリケーションの開発が盛んに⾏われるようになった • そこそこの規模の⼈数でそこそこの規模の JavaScript のコードを安全に書くために型の情報を使いたい • 組み込みの API が何を返してくるのか。ライブラリがどういう返り値を返してくるのか。 • ランタイムで実⾏した際に初めてランタイムエラーが起きるというのは防ぎたい。 • Uncaught TypeError: Cannot read property 'foo' of undefined • そのために JavaScript に静的型付けを加えた Alternative JavaScript としての TypeScript が⽀持される ようになった #hatenaintern)*)+

Slide 48

Slide 48 text

tsc: TypeScript compiler • マイクロソフトの TypeScript チームによって開発されている TypeScript を扱うためのツール • https://github.com/microsoft/TypeScript • TypeScript を JavaScript に変換(コンパイル)する^_機能とコンパイル時の静的型解析機能を提供している • --noEmit オプションを付けてると、静的型解析機能だけを⾛らせることができる • TypeScript から JavaScript への変換と⾔っても、型アノテーション等の削除がほとんど • JavaScript ランタイムが実⾏するときの振る舞いには影響を与えない^îと思っておいて良い • 多くのプロジェクトでは babel を使⽤していると思うので、@babel/preset-typescript を有効にした babel で変換を⾏う⽅が設定が複雑化しなかったりしてオススメ !" enum を使っているとコードが追加されるので影響を与える。enum は後述するオブジェクトと typeof などの組み合わせで代替可能なので、基本的には利⽤しないようにしていることが多 い。 !" JavaScript ランタイムが解釈できるように型情報を落として変換(コンパイル)する ≒ トランスパイラとしての役割 #hatenaintern)*)+

Slide 49

Slide 49 text

型宣⾔部分を読めるようになろう TypeScript の代表的な表現などを紹介していきます。 ちょっと⼿元で試したい⼈は公式の Playgroundがお⼿軽です。 #hatenaintern)*)+

Slide 50

Slide 50 text

変数宣⾔時の型アノテーション // JavaScriptͷ৔߹ const a = 'hello'; // TypeScriptͷ৔߹͸ม਺໊ͱ=ͷؒʹ:Λஔ͍ͯܕΞϊςʔγϣϯΛॻ͘ const a: ʲ͜͜ʹܕΞϊςʔγϣϯʳ = 'hello'; // ྫ͑͹ const a: string = 'hello'; これくらいだったら推論してくれるので、実際には書かないこと も多い #hatenaintern)*)+

Slide 51

Slide 51 text

代表的な表現 ① • プリミティブ型: string, number, boolean, null, undefined • 'hello', 12, true, false のような特定の値(リテラル型と呼びます)も使える const a: number = 10 const b: boolean = false const c: string = 11 // Type Error const d: "hello" = "hello" const n: null = null • 配列 const arr: string[] = ["hello", "world"] const arr2: Array = [1, 2, 3, 5, 8] const arr3: [string, number] = ["year", 2021] // λϓϧ(Tuple)ܕͱ΋ #hatenaintern)*)+

Slide 52

Slide 52 text

代表的な表現 ② • オブジェクト • JavaScript のオブジェクト同様に書いて、値を書くところに型を書く • キー名に ? を付けるとオプショナルなキーになって、省略可能になる。ST const person: { name: string age: number address?: string } = { name: "john", age: 21, } !" string | undefined のような記述と同じと紹介されることもある。値が⼊っているかどうかで JavaScript として実⾏した際の振る舞いが変わることがある(Object.keys() など)ので、厳密 には同じではないことに注意。特に TypeScript ^.^ で導⼊された exactOptionalPropertyTypes を有効にすると、tsc での型解析時の振る舞いも変わります。 #hatenaintern)*)+

Slide 53

Slide 53 text

type type を使うと型表現にエイリアスを付けられる type Person = { name: string age: number } type Team = Person[] #hatenaintern)*)+

Slide 54

Slide 54 text

Union Type (合併型) 複数の型のいずれかを満たす type Primitive = string | number | boolean | undefined | null type Color = "red" | "green" | "blue" | "yellow" | "purple" const a: Primitive = "hello" const b: Primitive = 10 const c: Color = "red" const d: Color = "black" // Type Error #hatenaintern)*)+

Slide 55

Slide 55 text

型ガード(Type Guard)に使える便利な JavaScript の演算⼦ JavaScript の演算⼦の中には有効に使うことで、静的解析時に TypeScript コンパイラによって便利に使⽤されるものがある。 typeof と in をここでは紹介します。これらは JavaScript のラン タイム上で動作することに注意。 #hatenaintern)*)+

Slide 56

Slide 56 text

typeof 演算⼦ • 値の型(ここでの"型"は JavaScript ランタイム上での型)を 'string', 'number', 'boolean', 'object', 'function', 'undefined' から返す console.log(typeof "hello world") // 'string' const n = 10 console.log(typeof n) // 'number' const p: Person = { name: "jonh", age: 20 } console.log(typeof p) // 'object' console.log(typeof undefined) // 'undefined' console.log(typeof null) // 'object' console.log(typeof ["a"]) // 'object' console.log(Array.isArray(["a"])) // true #hatenaintern)*)+

Slide 57

Slide 57 text

typeof 演算⼦の利⽤例 • 特に number や string であることに制限したいときに頻出 const func = (val: string | number) => { // Property 'toUpperCase' does not exist on type 'string | number'. // Property 'toUpperCase' does not exist on type 'number'. val.toUpperCase() if (typeof val === "string") { // ͜͜Ͱ͸val͸stringܕ return val.toUpperCase() } // ͜͜Ͱ͸val͸numberܕ return val.toFixed(2) } #hatenaintern)*)+

Slide 58

Slide 58 text

in 演算⼦ 値に特定のプロパティが有ることを判定する演算⼦。最近はブラウザが特定の API に対応しているかどうかを識別することにも使われる。 type Fish = { name: string swim: () => void } type Bird = { name: string fly: () => void } const move = (x: Fish | Bird) => { if ("swim" in x) { // swim͕༗Δͷ͸FishܕͷΈ return x.swim() } // ͳͷͰɺ͜͜ʹ౸ୡ͢Δͱ͸BirdܕͰ͋Δͱ෼͔Δ return x.fly() } #hatenaintern)*)+

Slide 59

Slide 59 text

Tagged Union Types で型ガードする • type や kind のようなプロパティにリテラル型を設定しておいて、その中⾝を === や switch を使って⽐較することで、型の絞り込みを⾏う • 個⼈的な感覚だと、in での絞り込みよりもこちらのパターンの⽅がよく利⽤する気がします • リテラル型の⽐較の⽅が書くのが簡単で、かつこちらのほうがより安全です。 type Fish = { kind: "fish" name: string swim: () => void } type Bird = { kind: "bird" name: string fly: () => void } const move = (x: Fish | Bird) => { if (x.kind === "fish") { return x.swim() } return x.fly() } #hatenaintern)*)+

Slide 60

Slide 60 text

as を⽤いた型アサーション(Type Assertion) • TypeScript によって推論された型を上書きしたいときに使う • 型キャストではない(ランタイム上での振る舞いがなんら変わ ることはない) • 多くの場合は害になるので、本当に必要な場合だけ利⽤する • 例えば、古い JavaScript のコードを移植するなど #hatenaintern)*)+

Slide 61

Slide 61 text

as を⽤いた型アサーション(Type Assertion) // ܕFooΛ௥Ճ͢Δ type Foo = { bar: number piyo: string } const something = (x: Foo) => { // ܕͱͯ͠͸߹͍ͬͯΔ͕ɺϥϯλΠϜ্Ͱͷ࣮ߦ࣌ʹ͸piyo͕undefinedʹͳΔͷͰΤϥʔ͕ى͖Δ return x.piyo.split(",") } const foo = {} as Foo // Fooͱͯ͠ѻ͑ΔΑ͏ʹ͓ͯ͘͠ // ࣮ࡍʹ͸barͳͲ͸ແ͍͕Fooͱͯ͠Ξαʔγϣϯ͍ͯ͠ΔͷͰ౉ͤΔ something(foo) #hatenaintern)*)+

Slide 62

Slide 62 text

const アサーション as const とすることで変数代⼊時などに変更不可能としてアサーションしてくれる。 const a = [1, 2, 3] // aͷܕ͸number[]ͱͳΔ const b = [1, 2, 3] as const // bͷܕ͸readonly [1, 2, 3]ͱͳΔ // readonly ͳ഑ྻʹ͸ push ΍ pop ͳͲͷมߋΛՃ͑Δϝιου͕ଘࡏ͠ͳ͍ a.push(4) // OK b.push(4) // NG type Pallet = { colors: Color } const setPallet = (p: Pallet) => { /* do something */ } const pallet = { color: "red", } // ͜͜ʹ as const Λ෇͚ͳ͍ͱ{ color: string }ͱਪ࿦͞ΕͯΤϥʔʹͳΔ setPallet(pallet) #hatenaintern)*)+

Slide 63

Slide 63 text

any と unknown TypeScript 上で不定な型を扱うための⽅法を紹介します。 #hatenaintern)*)+

Slide 64

Slide 64 text

any • 何らかの値が⼊っていることを⽰す。undefined からオブジェク トから関数まで何が⼊っていても良い。tsc が型の検証をしな いので、as 同様に避けられる場合は避ける。 let anything: any = {} // anyʹ͸ԿͰ΋୅ೖͰ͖Δ anything = () => {} anything = null anything.split(",") // anyͷ৔߹͸ϝιου΋ͳΜͰ΋ࢀরͰ͖Δ #hatenaintern)*)+

Slide 65

Slide 65 text

unknown • any 同様に何らかの値が⼊っている型。 • any と違い、unknown はメソッドにアクセスしようとすると検証時にエラーになる。 • unknown ≒ {} | null | undefined • 利⽤時に型ガードをして型を絞り込む const getObjectValues = (obj: {}) => Object.values(obj) // ݺͼग़͠ଆ͸unknownͳͷͰ޷͖ͳ஋Λ౉ͤΔ const stringifyForLog = (val: unknown) => { if (val === null || val === undefined) return "(anonymous)" return JSON.stringify(getObjectValues(val)) } #hatenaintern)*)+

Slide 66

Slide 66 text

type と interface • type はエイリアス • interface はオブジェクトに型を付ける別の⽅法。 • 既存のものに後からプロパティを追加できるのは interface • ライブラリが提供した interface を利⽤者が⾃由に拡張できる • type は別の名前を付ける必要がある • type と interface の使い分けはコード規約などで⾊々な流派や主張があ るので、郷に⼊れば郷に従ってください #hatenaintern)*)+

Slide 67

Slide 67 text

type と interface Type • Type は同じ型定義名での拡張ができない type Blog = { title: string content: string } // NG: Duplicate identifier 'Blog'. type Blog = { tags: string[] } // OK type NewBlog = Blog & { tags: string[] } Interface • Interface は拡張可能 • 例えば • ES5655 では、Array に at() メソッドが追加された • TypeScript I.K での型定義を確認すると、 実際に Array 型を拡張 している // lib.es5.d.ts interface Array { length: number toString(): string ... } // lib.es2022.array.d.ts interface Array { at(index: number): T | undefined } #hatenaintern)*)+

Slide 68

Slide 68 text

関数 既にサンプルでは何度も出てきているけど、引数や返り値の型の書き⽅のパターンたち紹介しておきます。 const f = (x: string): number => { return x.length } // ಛʹreturnΛ͠ͳ͍৔߹͸ฦΓ஋ʹvoidΛࢦఆ͢Δ const a: () => void = () => { console.log("a") } // ΦϓγϣφϧͳҾ਺͸keyʹ?Λ෇͚Δ // ਪ࿦͞ΕΔ΋ͷͰྑ͍ͳΒฦΓ஋ͷܕ͸লུՄ const b = (n?: number) => `${n}` // Rest ParametersΛड͚औΔ৔߹͸͜͏͍͏ײ͡ const c = (...texts: string[]) => { return texts.join("|") } #hatenaintern)*)+

Slide 69

Slide 69 text

型引数(Generics) • 関数の返り値の型に関する制約を外から与えて、関数内部で利⽤できる。 • よくよく型定義などを⾒ると定義されていて利⽤可能だけど、気付いていないということもよくある… • TypeScript で querySelector メソッドを使うときに型引数を指定する - Hatena Developer Blog const getJSON = (url: string): Promise => { // res.json͸anyͱͳΒͣʹܕҾ਺Ͱ౉͞Εͨ΋ͷͱղऍ͞ΕΔ return fetch(url).then((res) => res.json()) } // ͜͜Ͱusers͸User[]ʹͳΔ const users = await getJSON("/api/users") // ͜͜Ͱblogs͸Blog[]ʹͳΔ const blogs = await getJSON("/api/blogs") #hatenaintern)*)+

Slide 70

Slide 70 text

続: 型引数 • extends を使うと指定した型/インターフェースを満たすように指定できる • 型引数をそのまま関数の引数の型に使うことで静的解析時に呼び出し元の引 数の型から返り値を推論してくれる const echo = (text: T): T => { return text } const a = echo("foo") // a ͷܕ͸ 'foo' const str: string = "foo" const b = echo(str) // b ͷܕ͸ 'string' #hatenaintern)*)+

Slide 71

Slide 71 text

never • 絶対に発⽣‧到達しないことを表す型 • 例えば無限ループがある関数は絶対に完了しない関数なので返り値は never になる • void は代⼊可能MNだが、never には never のみ代⼊可能(つまり変数には基 本的に代⼊出来ずにエラーになる) const infiniteLoop = (): never => { while (true) {} } !" strictNullChecking が有効になっていない場合のみ代⼊可能。⼀⽅で JavaScript のランタイム上では undefined なので、代⼊することはほとんどないでしょう。 #hatenaintern)*)+

Slide 72

Slide 72 text

never を⽤いた網羅性チェック • never を⽤いることで型ガードなどでの網羅性をチェックできる type Color = "red" | "yellow" | "purple" const magical = (color: Color) => { switch (color) { case "red": return "tomato" case "yellow": return "banana" default: // શͯͷcase͕໢ཏ͞Ε͍ͯΕ͹͜͜ʹ౸ୡ͢Δ͜ͱ͸ͳ͍ // ͜ͷ৔߹͸'purple'͕୅ೖ͞ΕΔՄೳੑ͕͋ΔͷͰɺ੩తղੳ࣌ʹΤϥʔʹͳͬͯݕग़Ͱ͖Δ const val: never = color throw new Error(`"${color}" is not implemented`) } } #hatenaintern)*)+

Slide 73

Slide 73 text

ざっとだけ眺めるコーナー • ここまでを理解したときに読むと便利なコーナーです • ちょっとしたテクニックと合わせて書いておきます #hatenaintern)*)+

Slide 74

Slide 74 text

Readonly / ReturnType • Readonly はオブジェクトや配列の要素を読み込み専⽤にできる • 再帰的には適⽤されないことに注意 type PersonRo = Readonly • ReturnType は関数の返り値型として得る type Func = () => string type ReturnVal = ReturnType // ReturnVal͸stringͱղऍ͞ΕΔ #hatenaintern)*)+

Slide 75

Slide 75 text

typeof 演算⼦ • こちらは前述のものとは違うTypeScript 組み込みのもの。型ア ノテーションを書く場所で利⽤する。 • JavaScript で宣⾔された値を静的解析して型を得る const hello = () => "hello" type Func = typeof hello type P = ReturnType // P͸stringͱղऍ͞ΕΔ #hatenaintern)*)+

Slide 76

Slide 76 text

keyof 演算⼦ オブジェクトの key を列挙して union type として得られる const config = { checkJs: false, force: true, } // typeofͰconfigͷܕΛಘͯɺkeyofͰͦͷΦϒδΣΫτͷܕͷΩʔΛྻڍ͢Δ type ConfigKey = keyof typeof config // "checkJs" | "force" const getConfigVal = (key: ConfigKey) => config[key] getConfigVal("ignore") // Τϥʔͱͯ͠ݕग़Ͱ͖Δ #hatenaintern)*)+

Slide 77

Slide 77 text

Indexed Access Types JavaScript で配列やオブジェクトに [] を使って値を取り出すように型でも同 じようなことが出来る。typeof や keyof などと組み合わせることで JavaScript で定義した配列やオブジェクトの値から列挙した型などを⽣成で きる。 type Person = { name: string age: number } type Age = Person["age"] // number const signalColors = ["red", "blue", "yellow"] as const // as const͠ͳ͍ͱstring[]ʹͳΔ // Arrayʹ[number]ͰΞΫηε͢Δͱ഑ྻͷ஋Λྻڍͨ͠ܕΛಘΒΕΔ type Signal = typeof signalColors[number] // 'red' | 'blue' | 'yellow' #hatenaintern)*)+

Slide 78

Slide 78 text

交差型(Intersection Types) 複数の型を組み合わせて型を⽣成する。例えばユーザーの権限に 応じてレスポンスの型を変える場合はこういう感じ。 type Blog = { title: string; domain: string } type Editor = { editable: true; authority: ["edit"] } type Owner = { editable: true; authority: ["edit", "publish"] } type ResponseForEditor = Blog & Editor type ResponseForOwner = Blog & Owner #hatenaintern)*)+

Slide 79

Slide 79 text

Conditional Types JavaScript の三項演算⼦と同じことが型表現の中でも出来る。 condition ? trueExpression : falseExpression // JavaScriptͷࡾ߲ԋࢉࢠ // SomeType͕OtherTypeΛຬͨͤ͹TrueTypeΛಘΔ SomeType extends OtherType ? TrueType : FalseType; Overloads を使わずに関数の引数に応じて返り値の型を変える declare function getID( fancy: T ): T extends true ? string : number const stringReturnValue = getID(true) const numberReturnValue = getID(false) const stringOrNumber = getID(Math.random() < 0.5) #hatenaintern)*)+

Slide 80

Slide 80 text

Overloads • 関数が引数の数や型などに依って振る舞いが変わることを記述する⽅法 • 引数と返り値を複数宣⾔した後に実装を書く • 最後の実装は全ての Overloads を満たす必要がある function request(url: string): Promise function request(url: string, onlyIsOk: true): Promise function request(url: string, onlyIsOk = false) { const promise = fetch(url) if (onlyIsOk) return promise.then((res) => res.ok) return promise } #hatenaintern)*)+

Slide 81

Slide 81 text

Overloads と関数の型宣⾔ • 基本は短く書くのを使っておけば良いです type f = (x: number) => number • 同様のことをこのようにも書ける type f = { (x: number): number } • Overloads を型宣⾔と共に書きたい場合は後者を使う type func = { (x: number): number (x: string): string } #hatenaintern)*)+

Slide 82

Slide 82 text

React について(,-min) #hatenaintern)*)+

Slide 83

Slide 83 text

この章では React に関する最低限の読み書きが出来るようになる ことを⽬指します。 独りで開発していても、数千の開発者と共同開発していても、 React の開発体験は同じです。個⼈、チーム、⼤規模な組織によっ て書かれさまざまなコンポーネントを、シームレスに組み合わせ ながら開発できる。それが React の設計理念です。 https://ja.react.dev/ #hatenaintern)*)+

Slide 84

Slide 84 text

イメージしてみる さて、⽂字だけでは想像しにくいので実際のコードを⾒ながら説明していきたいと思います。 例えば、以下のような TODO アプリを作ろうとします。 #hatenaintern)*)+

Slide 85

Slide 85 text

素の JavaScript #hatenaintern)*)+

Slide 86

Slide 86 text

素の JavaScript (動的⽣成)の問題点 • どういう成果物が⽣成されるのかイメ ージしづらい • UI の追加や⽣成がその場その場で書か れがちで集約化が難しくなりがち • テストやその他設計上の⼯夫なども難 しくなる #hatenaintern)*)+

Slide 87

Slide 87 text

React #hatenaintern)*)+

Slide 88

Slide 88 text

   React (宣⾔的 UI)の利点 • UI 部分を宣⾔的に記述する⽅法のこと • ウェブアプリケーションのリッチ化や スマートフォンアプリの登場で、GUI を動的に構築‧変更する重要度と求め られる GUI の複雑性が格段に向上した という歴史がありました #hatenaintern)*)+

Slide 89

Slide 89 text

魂が震える&'仮想(Virtual) DOM • React の内部では実際の DOM23ではなく、DOM と対になる単純な構造体を持っている。これが仮想 DOMと呼ば れている。 • React では状態の変更を検知すると、変更前後の仮想 DOM の差分を計算し、その差分がある部分だけを実際の DOM に適応する。 • ブラウザのレンダリングコストを抑えつつも、開発者は UI をそのまま記述するだけで良いという仕組みを実 現している。 • リストなどで順番が⼊れ替わる際などは key という属性を与えることでその⼦要素が同じであることを⽰すこと が出来る https://ja.reactjs.org/docs/reconciliation.html !" Document Object Model の略。HTML や XML ⽂書の構造を操作するためのプログラミングインターフェイスのこと。 !" https://zenn.dev/mizchi/books/"c66c7!"f6cc96:c!;b< #hatenaintern)*)+

Slide 90

Slide 90 text

React の書き⽅‧読み⽅ #hatenaintern)*)+

Slide 91

Slide 91 text

JSX JSX は JavaScript の拡張構⽂

My name is Clementine!

• React DOM は HTML の属性名ではなく、キャメルケースの命名 規則を使⽤します。(aria-* や data-* 属性は例外です。) • class も className と記述されますが、これは class が JavaScript において予約語であるためです。 #hatenaintern)*)+

Slide 92

Slide 92 text

クラスコンポーネントと関数コンポーネント React ではコンポーネントの書き⽅が 2 種類あります。 クラスコンポーネント class HelloMessage extends React.Component { render() { return
Hello {this.props.name}
} } 関数コンポーネント const HelloMessage = ({ name }) => { return
Hello {name}
} #hatenaintern)*)+

Slide 93

Slide 93 text

クラスコンポーネントと関数コンポーネント 関数コンポーネントを使⽤することが推奨されている。 クラスコンポーネント React アプリでのエラーを扱うのに Error Boundary という仕組みで使うこともある。 関数コンポーネント (Function Component) • React Component は関数を⽤いて表現すること で関数型プログラミングの概念を利⽤できるよ うにしている • UI = f(state) • Presenter/View などの層に分けた後に、それら を組み合わせた合成関数として React Component が表現出来る • Unit Test などが簡単になった #hatenaintern)*)+

Slide 94

Slide 94 text

Function Component と TypeScript • TypeScript で JSX を記述する場合は拡張⼦を.tsxにする • 型推論に任せると () => JSX.Elementになる。保守性などを考 えるとReact.FC を明⽰するのが良いOP type Props = { name: string } const Welcome: React.FC = ({ name }) => { return

Welcome {name}

} !" React Component として不正なundefinedを返すことなどを検出してくれます #hatenaintern)*)+

Slide 95

Slide 95 text

Props と State • React Component には値を持つ⽅法が⼤きく 2 つある • 関数の引数として受け取るPropsと内部状態を保持するState #hatenaintern)*)+

Slide 96

Slide 96 text

Props を渡す/受け取る • 受け取る側は関数の第 1 引数でオブジェクトとして受け取る type Props = { name: string } const Welcome: React.FC = ({ name }) => { return

Welcome {name}

} • 渡す側(親側)は JSX で属性値として渡す。オブジェクトのキーと値がそ れぞれ属性名と値に対応する //

Welcome John

#hatenaintern)*)+

Slide 97

Slide 97 text

Function Component と Hooks • Hooks は React -..0(2019 年)から追加された機能。 • それまでは関数コンポーネントは、SFC ( Stateless Functional Component ) と呼ばれていた。 • 関数コンポーネントに状態を持つことができるようになった。 #hatenaintern)*)+

Slide 98

Slide 98 text

状態(State)を扱う • useStateという特殊な関数 を使うことで状態を持つこ とが出来る • こういったuseから始まる関 数は React でフック (Hooks)と呼ばれる特別な 関数 const Counter = () => { const [count, setCount] = useState(0) const increaseCount = () => setCount((prevCount) => prevCount + 1) return (
Χ΢ϯτ: {count} Χ΢ϯτ
) } #hatenaintern)*)+

Slide 99

Slide 99 text

Hooks の掟 Rules of Hooks ‒ React • 名前はuseから始める • トップレベルで呼ぶ • ifの中などで呼ばない • early return する前に必ず呼ぶ これらはeslint-plugin-react-hooksで検出してくれるように出来る #hatenaintern)*)+

Slide 100

Slide 100 text

代表的な組み込み Hooks の紹介 #hatenaintern)*)+

Slide 101

Slide 101 text

useState • useState()で初期値を与えることが出来、返り値のタプルの 1 つ⽬が 現在の値で、2 つ⽬が setter になっている • 返り値がタプル(配列)なので、好きな名前をつけるのが簡単 • setter を呼ぶと内部状態が更新されたことが Component に通知さ れて、仮想 DOM の再⽣成、⽐較、レンダリングの更新が⾏われる • 推論できないものを型指定したい場合は型引数を⽤いることが出来る const [color, setColor] = useState("red") #hatenaintern)*)+

Slide 102

Slide 102 text

useStateの setter について • 新しい値を渡す const [color, setColor] = useState("red") const change2Blue = () => setColor("blue") • 直前の値を利⽤して新しい値を決定する const [count, setCount] = useState(0) const increaseCount = () => setCount((prevCount) => prevCount + 1) #hatenaintern)*)+

Slide 103

Slide 103 text

useEffect • React Component 内外部システムと同期を⾏うためのフック • 例えば • API からデータを取得して Component の状態を更新する • イベントハンドラを登録する • jQuery のコードを呼び出すなど #hatenaintern)*)+

Slide 104

Slide 104 text

setInterval でタイマーと同期する const Timer = () => { const [timer, setTimer] = useState(0) setInterval(() => { setTimer((prevTimer) => prevTimer + 1) }, 1000) return
ܦա࣌ؒ: {timer}
} • コンポーネントがレンダリングされるたびにsetIntervalが呼ばれてしまう #hatenaintern)*)+

Slide 105

Slide 105 text

useEffectの中でsetIntervalを呼ぶ const Timer = () => { const [timer, setTimer] = useState(0) useEffect(() => { setInterval(() => { setTimer((prevTimer) => prevTimer + 1) }, 1000) }, []) return
ܦա࣌ؒ: {timer}
} • useEffect の依存配列に空配列を渡すことで、Component のマウント時にのみ実⾏される ようになる #hatenaintern)*)+

Slide 106

Slide 106 text

useEffectと依存配列 • 依存配列で副作⽤のタイミングをコントロールできる • 第 2 引数に依存を表す配列を渡すことができる • Component のマウント時 • useEffect(೚ҙͷॲཧؔ਺, []) • 空の配列を渡すことで、マウント時に副作⽤を実⾏できる • ある state などが更新された際に副作⽤を呼び出すには? • useEffect(೚ҙͷॲཧؔ਺, [state]) • 副作⽤に関連する state の配列を渡すことで Component の状態に応じて副作⽤を実⾏できる #hatenaintern)*)+

Slide 107

Slide 107 text

タイマーのカウント数を変更する例 const Timer = () => { const [timer, setTimer] = useState(0) const [count, setCount] = useState(1) useEffect(() => { setInterval(() => { setTimer((prevTimer) => prevTimer + count) }, 1000) }, []) return ( <> setCount(c => c + 1)}> Χ΢ϯτ਺Λ૿΍͢ {count}
ܦա࣌ؒ: {timer}
> ) } count を増やしても timer の増加数は変わらない。 #hatenaintern)*)+

Slide 108

Slide 108 text

useEffectと依存配列 • 依存配列を設定することで、それぞれの依存配列内のいずれかの値が変更されたとき>?のみ副作⽤が呼び出されるように出来る useEffect(() => { const id = setInterval(() => { setTimer((prevTimer) => prevTimer + count) }, 1000) return () => { clearInterval(id) } - }, []) + }, [count]) • 依存配列は中⾝が空の配列[]を指定すると、Component のマウント時@Aに限定することが出来る !" Component に対応した DOM が挿⼊(初回描画)されることをマウント(mount)、その DOM が削除されることをアンマウント(unmount)と呼びます !" 変更されたことを検知する⽐較の詳細については付録「Hooks の依存の⽐較とオブジェクトの同⼀性について」を参照 #hatenaintern)*)+

Slide 109

Slide 109 text

useEffect とクリーンナップ • useEffect は関数を返すことでその関数が次の副作⽤が呼び出される前:;に実⾏される • これを利⽤すると副作⽤のクリーンナップが出来る 前回の setInterval の例をクリーンナップを追加すると以下のようになる useEffect(() => { const id = setInterval(() => { setTimer((prevTimer) => prevTimer + count) }, 1000); return () => { // clearInterval ͰλΠϚʔΛఀࢭ͢Δؔ਺ clearInterval(id) } }, [count]) !" 空配列を指定している場合などはアンマウント時にも実⾏される #hatenaintern)*)+

Slide 110

Slide 110 text

独⾃フック(Custom Hook) • use から名前が始まり、内部で他の Hook を呼び出せる関数のこと • 複数の Hooks を組み合わせたり、Component の振る舞いを共通化して、1 つの関数に切り出すときなどに利⽤する const useGetStatus = ({ userId }) => { const [status, setStatus] = useState(null) useEffect(() => { const handler = (user) => { setStatus(user.status) } Api.subscribe(userId, handler) return () => Api.unsubscribe(userId, handler) }) return status } #hatenaintern)*)+

Slide 111

Slide 111 text

独⾃フックについての Tips • 独⾃フックに Component の役割を共通化することで切り出した機能のみをテスト可能になる • 独⾃フックで返すオブジェクトや関数などは useState や useMemo、useEffect、useCallback で 同⼀性を担保しておく • 特に関数についてはうっかり素朴に関数オブジェクトを返してしまいがちなので、気にしておく と吉ij • 独⾃フックを呼び出す側が同⼀性を保持するためにメモ化する(ことを検討する)必要が出てき てしまう • 結果、そのままでは利⽤できずに useEffect の依存配列に渡すこととなり、結果的に再利⽤性 も下がってしまう !" useCallback はとにかく使え! 特にカスタムフックでは - uhyo/blog #hatenaintern)*)+

Slide 112

Slide 112 text

お疲れさまでした • TypeScript と React についてザッと概観についてお伝えしました • これだけで皆さんが書くのは難しいかもしれませんが、 TypeScript や React に挑戦するきっかけになってくれればと思っ ています • JavaScript やフロントエンドの世界は更に広がっています。ウェ ブブラウザを中⼼にあらゆるところでユーザーの⽬に直接触れる ことの多いフロントエンドの世界を楽しんでください。 #hatenaintern)*)+

Slide 113

Slide 113 text

付録 本編に⼊り切らなかった踏み込んだ補⾜や解説について書 いています。興味があれば読んでください的なコーナーで す。 ⽬次 • Arrow Function について • useState の state を更新する際の注意 • Hooks の依存の⽐較とオブジェクトの同⼀性について • useMemo や useCallback を⽤いたパフォーマンス改善 • React Component 内で DOM にアクセスする • JavaScript をウェブアプリケーションで提供する際に使 ⽤するツールチェインについて • JavaScript やフロントエンド周辺の主なツールについて • JavaScript やフロントエンド周辺技術の標準(化)につい て #hatenaintern)*)+

Slide 114

Slide 114 text

Arrow Function に置き換え出来ないケース • function を使ってコンストラクタ関数にしている(new している) • arguments を参照している • this を参照している • このケースについて解説します #hatenaintern)*)+

Slide 115

Slide 115 text

Arrow Function と this • Arrow Function と function キーワードで⽣成した関数とでは 主に this の扱いが⼤きく異なる • このことによって単純な置き換えが不可な場合がある #hatenaintern)*)+

Slide 116

Slide 116 text

this のスコープの違いについて • function を⽤いた関数の場合 const person = { name: "chris", say: function () { setTimeout(function () { console.log(`I'm ${this.name}`) }, 100) }, } person.say() // I'm • 関数の中の this は呼び出し元のオブジェクトになる。この場合は window.setTimeout(window は省略できる) からの 呼び出しなので、 window になっている • https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/ setTimeout#thethisproblem #hatenaintern)*)+

Slide 117

Slide 117 text

this のスコープの違いについて • Arrow Function だとスコープが外と同じになる const person = { name: "chris", say: function () { setTimeout(() => { console.log(`I'm ${this.name}`) }, 100) }, } person.say() // I'm chris #hatenaintern)*)+

Slide 118

Slide 118 text

useState の state を更新する際の注意 新しい値を渡すときの落とし⽳ const [count, setCount] = useState(0) const increaseCount = () => setCount(count + 1) としてしまうと、 const increaseDouble = () => { increment() increment() } のようなものを作ったときに、increaseDouble を呼んでも 1 しか増えないという現象に遭遇するでしょ う。 #hatenaintern)*)+

Slide 119

Slide 119 text

関数内の変数スコープと useState 例えば、count が 5 であるとすると const Component = () => { const [count, setCount] = useState(0) // ͜͜Ͱม਺count͸είʔϓ಺Ͱݻఆ͞ΕΔ(5) const increaseCount = () => setCount(count + 1) // ↑Ͱݻఆ͞Εͨcount + 1(=6)ΛsetCountʹ౉͢ const increaseDouble = () => { increment() // ↑Ͱ࡞ͬͨؔ਺ΛݺͿͱɺ6͕setCountʹ౉͞ΕΔ increment() // ͜ͷؔ਺ΛݺͿͱ͖΋ɺ6͕setCountʹ౉͞ΕΔͷͰɺ஋͸6ͷ·· } } #hatenaintern)*)+

Slide 120

Slide 120 text

コールバック関数を渡す⽅法で書き直す 例えば、count が 5 であるとすると const Component = () => { const [count, setCount] = useState(0) // ͜͜Ͱม਺count͸είʔϓ಺Ͱݻఆ͞ΕΔ(5) const increaseCount = () => setCount((prevCount) => prevCount + 1) // prevCount͸ݺͼग़࣌͠ͷ௚લͷ஋ const increaseDouble = () => { increment() // ͜͜ͰݺͿͱɺprevCountʹ͸5͕ೖ͍ͬͯΔͷͰɺprevCount+1=6͕setCount͞ΕΔ increment() // ͜͜ͰݺͿͱɺprevCountʹ͸6͕ೖ͍ͬͯΔͷͰɺprevCount+1=7͕setCount͞ΕΔ } } 更新後の state の値が更新前の値に依存している場合は、コールバッ ク関数を渡す形式を使うようにしましょう。 #hatenaintern)*)+

Slide 121

Slide 121 text

Hooks の依存の⽐較とオブジェクトの同⼀性 について プリミティブな値のときは === で⽐較されるが、オブジェクトや配列は Object.is で⽐較され る。つまり、中⾝が同じオブジェクトなだけでは異なる値として認識されることに注意。 const a = "foo" const b = "foo" console.log(a === b) // true const objA = { a: "foo", b: 10 } const objB = { a: "foo", b: 10 } console.log(objA === objB) // true console.log(Object.is(objA, objB)) // false #hatenaintern)*)+

Slide 122

Slide 122 text

依存配列にオブジェクトを⼊れるケースについて const config = { theme: "sports" } useEffect(() => { loadConfig(config).then(() => {}) }, [config]) のような場合にはレンダリングの度に、config が再⽣成される。よって、 異なる値として認識されてしまい、毎回副作⽤が呼び出されてしまう。こ のような例の場合は依存に [config.theme] という⾵に値を書いてしまっ ても良いが、依存するオブジェクトについて知っている必要があるので難 しい。 #hatenaintern)*)+

Slide 123

Slide 123 text

シンプルな回避策 • ⼀番簡単な回避策は Component の外で初期化してしまえば良い const config = { theme: "sports" } function Component() { useEffect(() => { loadConfig(config).then(() => {}) }, [config]) } • ⼀⽅で、この世には Props を利⽤してオブジェクトを⽣成するなどもよ くあるので、この⽅法が使えることは少ない #hatenaintern)*)+

Slide 124

Slide 124 text

オブジェクトの再⽣成を回避するために useMemo を利⽤ する • useMemo は React に組み込まれている Hooks の 1 つで、その名の通り、値をメモ化DEすることが出来ます 英⽂章を⼦要素に持ち+,、その⽂章中の単語数を数える Component を書いてみる function WordCount({ children }) { // text͸stringͳͷͰɺͦͷ··ґଘ഑ྻʹग़དྷΔ const words = useMemo(() => children.split(" "), [text]) useEffect(() => { alert(`୯ޠ਺͸${words.length}Ͱ͢`) }, [words]) } !" Component の中の⼦要素は children という名前で受け取ることが出来る。Lorem ipsum dolor sit amet のような形で呼び出す。 !" メモ化はパフォーマンス改善のために計算結果をキャッシュしたりすること #hatenaintern)*)+

Slide 125

Slide 125 text

useCallback で関数をメモ化する • 値同様に関数をメモ化したい場合は useCallback を利⽤する • メモ化しないと毎レンダリング時に関数オブジェクトが⽣成 されてしまう const handler = useCallback((val) => alert(val), []) useEffect(() => { Api.notification.subscribe(handler) }, [handler]) #hatenaintern)*)+

Slide 126

Slide 126 text

useMemo や useCallback を⽤いたパフォーマ ンス改善 • useMemo や useCallback は値や関数のメモ化によって useEffect などの不要な呼び出しを抑制することによってパフォーマン スの改善を期待できる • 特に useMemo では依存に使わない場合でも、処理の重い計算結果をメモ化することでレンダリング時のブロックを抑えること も出来る function Component ({a, b}) => { const resultA = useMemo(() => superDuperHighCostCalculation(a), [a]); const resultB = useMemo(() => superDuperHighCostCalculation(b), [b]); return <> {a}͔ΒʮΊͪΌͪ͘Όॏ͍ܭࢉʯΛͨ݁͠Ռ: {resultA} {b}͔ΒʮΊͪΌͪ͘Όॏ͍ܭࢉʯΛͨ݁͠Ռ: {resultB} > } #hatenaintern)*)+

Slide 127

Slide 127 text

React Component 内で DOM にアクセスす る • React に組み込まれている、参照を保持できる ref オブジェクトを作成する useRef を使う • ref オブジェクトは current プロパティに現在の値を持っている const textInput = useRef(null) const submit = (e) => { e.preventDefault() if (textInput.current.value.length < 100) return alert("101จࣈҎ্͕ඞཁͰ͢") createPost({ body: textInput.current.value }) } return ( ) #hatenaintern)*)+

Slide 128

Slide 128 text

DOM へのアクセスを避ける⽅が良い • DOM に直接アクセスすると、React の制御外のところで値が取得されたり変更されたりすることにな る。 • ライブラリの都合などで本当に必要なときのみにしておくと良い const [text, setText] = useState("") const submit = (e) => { e.preventDefault() if (text < 100) return alert("101จࣈҎ্͕ඞཁͰ͢") createPost({ body: text }) } return ( setText(e.target.value)} /> ) #hatenaintern)*)+

Slide 129

Slide 129 text

JavaScript をウェブアプリケーションで提供 する際に使⽤するツールチェインについて TypeScript/JavaScript を実際のウェブアプリケーションでユーザ ーのウェブブラウザで実⾏する際には、いくつかのツールを組み 合わせて変換したりしたものを提供する。 #hatenaintern)*)+

Slide 130

Slide 130 text

TypeScript のコードがユーザーに届くまでの例 • TypeScript→JavaScript • tsc でも出来るが、babel で変換することも多い • JavaScript→ 対応ブラウザへの互換性がある JavaScript • API によってはサポートしているブラウザでそのままでは動かないことがあるので、babel を⽤いて、必要な polyfill などを組み⼊れる • TS→JS の変換も babel で⾏う場合は、それぞれ必要なプラグインを⼊れて実⾏ • 個別の JavaScript ファイル → 単⼀の JavaScript ファイル • webpack などで JavaScript のファイルを統合(bundle)する。ES Modules を全ての環境でサポートしていれば不要という説もあるが、1 つ のファイルになっている⽅がパフォーマンスなどの観点で有利なこともある • JavaScript ファイル → 圧縮された JavaScript(+sourcemap) • ユーザーの⼿元に届くときには、容量を減らしたりするために minify©™されたコードにすることが多い。 • そのままでは読むのが困難なので、開発者向けやエラートラッキングのために元の TS/JS との対応を記録したものが sourcemap !" 難読化とは異なり、変数名や関数名などを 1 ⽂字に置き換えるなどして容量の削減を図る。webpack に組み込まれている Terser では、TreeShaking という不要なコードの削除や不要な分 岐の削除なども⾏われる。 #hatenaintern)*)+

Slide 131

Slide 131 text

webpack だと‧‧‧ • TypeScript から JavaScript までの変換と互換性のための操作 は babel(babel-loader 経由で利⽤)で⾏う • ファイルの結合などは webpack の主たる機能 • 容量削減は Terser(webpack-terser-plugin 経由で利⽤)で⾏う #hatenaintern)*)+

Slide 132

Slide 132 text

create-react-app について • React を使ったプロジェクトの作成、関連ツールの導⼊‧実⾏を⾃動化してくれる • 基本的にはまずはこれを使っておけば OK • webpack, babel, eslint など諸々の設定の⽣成も⾃動で⾏われます • npm start でローカル開発環境の⽴ち上げ、npm test でユニットテストが実⾏できる • 詳細は内部で使われているreact-scriptsに集約されている • create-react-app で利⽤するツールは定期的に⾒直されたりしていて、常に最新の React コミ ュニティの知⾒が活かされている • create-react-app をウォッチすることでそういった最新の動向というものにも触れられるの で、React の周辺について知りたい⼈はこのリポジトリを覗いてみるのがオススメ #hatenaintern)*)+

Slide 133

Slide 133 text

JavaScript やフロントエンド周辺技術の標準 (化)について 主にブラウザ上で動く JavaScript や DOM、HTML、CSS などの API はそれぞれ 以下のような仕様でそれぞれ標準化されている • ECMAScript • WHATWG HTML Living Standard • WHATWG DOM Living Standard • W9C CSS Specifications #hatenaintern)*)+

Slide 134

Slide 134 text

なぜ仕様を知るのか • その API の振る舞いについて正しい振る舞いを定義しているのは仕様である。 • 世の中に公開されている情報が数多くあるが、その情報の正しさを最終的に保 証するための情報源が仕様である。 • 例えば JavaScript や CSS などの振る舞いがウェブブラウザ間で異なる場合、 その時に正しさを保証してくれるのは仕様である。 • 実際、フロントエンドを中⼼に⾊んなことをしていると、仕様とブラウザの 実装に差異があることに遭遇して、報告することがあります~。 !" ウェブブラウザにバグ報告をするときにやること - ぱすたけ⽇記 #hatenaintern)*)+

Slide 135

Slide 135 text

ECMAScript について • JavaScript の標準仕様は ECMAScript という名前で、Ecma InternationalCDの中の Technical Committee DH(TCDH)という技術委員会が中⼼に策定されている。 • 現在の仕様はhttps://github.com/tcDH/ecmacdcで管理されていて、 常に最新版が公開され 続けている。 • こういう常に最新版が公開されている形式を Living Standard と呼びます。 • 毎年 6 ⽉頃のミーティングで ECMAScript cÑcÖ のようなタグが打たれて、バージョン番号が 付与したものも公開されますが、その時点でのスナップショットであり、そのバージョン⾃ 体にはあまり意味はないですCC !! babel や tsc の設定で利⽤することもありますが、仕様としてはそんなに気にする必要はないです。このバージョンとは独⽴して、仕様の提案が進むタイミングでそれぞれの処理系におい て実装が⾏われて、利⽤可能になるからです。また、後述しますが polyfill などによって実⾏可能になることもあります。 !" Ecma International は C#などの標準化作業も⾏っている #hatenaintern)*)+

Slide 136

Slide 136 text

ECMAScript の Stage • ECMAScript には⾃由に提案(proposal)を出すことが出来る • Github 上で proposal が公開され、issue や PR や TCQR のミーティング などでの議論を経て、Stage が上がったりする • 責任者であるチャンピオンのステージを進めたいという意思も必要 • 気になるプロポーザルがなかなか取り込まれない場合は、Issue やミーテ ィングノートを⾒るとヒントがあるかも • それらは次のようなステージを経て、Stage å になった後に ECMAScript の 仕様に取り込まれる。 #hatenaintern)*)+

Slide 137

Slide 137 text

Stage の詳細 ステージ ステージの概要 ( アイデア 状態 / 機能提案 4 機能の仕様書ドラフトを作成した状態 A 仕様としてはほぼ完成していて、ブラウザの実装やフィードバックを求め る段階 Y 仕様策定が完了し、2 つ以上の実装YbYcが存在している状態 ECMAScript · JavaScript Primer #jsprimer !" 主に Stage+ 以前時点での実装などはその後の仕様変更などの可能性もあるので、ブラウザの開発者向けフラグを有効にしているときだけ使えたり、prefix を付けた名前で API が提供され ることもある。それらも実装として許容されるなどの緩さはある。 !" 2 つ以上の JavaScript のランタイムに実装が存在しているということが求められる。主なランタイムは Google Chrome や Node.js で使⽤されている V_ や Safari で使⽤されている JavaScriptCore、Firefox で使⽤されている SpiderMonkey、Facebook が開発している Hermes などがある。 #hatenaintern)*)+

Slide 138

Slide 138 text

ECMAScript の仕様と実装 • Stage ( になったり、ECMAScript に取り込まれていなくても、ウェブブラウザなどに実装されて使⽤出来るようにな ることがある • 逆に実装されていないと、仕様に取り込めない • その状態で積極的に使⽤することでプロポーザルにフィードバックをすることができる • また、実装がない状態でもコミュニティや core-js によってできるまた polyfill 実装が提供されたり、babel による トランスパイルなどでアプリケーション内で使⽤できるようになることがある • それらを実⽤することでもプロポーザルにフィードバックができたり、有⽤性を⽰すことが出来る • ECMAScript のプロポーザルは常に全てが Open で、それに関わるディスカッションも Open なので、興味のある提案 があったら、是⾮覗いてみて、使⽤し、ポジティブな点(úや問題点があれば是⾮フィードバックをすることで、未来の JavaScript 環境をよくすることに繋がります。 !" ECMAScript のプロポーザルは多くの場合、その提案が「どのようなモチベーションがあるのか」、「どのような問題を解決するのか」、「どのようなユースケースがあるのか」などが記 されているので、その有⽤性などを⽰すことも貢献に繋がります。 #hatenaintern)*)+

Slide 139

Slide 139 text

ウェブブラウザ上の JavaScript と ECMAScript • ウェブブラウザ上で実⾏できる JavaScript は ECMAScript の仕様に含まれているものだけではない • 例えば location.assign はWHATWG HTML Living Standard、document.querySelector はWHATWG DOM Living Standardで定義されている • WHATWG という組織^_によって更新‧管理されている HTML/DOM それぞれの Living Standard な仕様によ って管理されている。^k • ECMAScript の仕様ではないので、HTML や DOM を扱わない環境(Node.js など)では実⾏できない !" 以前は W(C によって HTML2、HTML2.5 などが策定されていたが、399 年 5 ⽉に WHATWG 側に歩み寄ることを表明、2021 年 1 ⽉ 29 ⽇には WHATWG が公開している Living Standard を 正式に W(C の勧告とした。/参考:[https://future-architect.github.io/articles/vwv5wxv5a/ どうして HTML2 が廃⽌されたのか | フューチャー技術ブログ] !" 2004 年に Apple‧Mozilla‧Opera の 3 社によって設⽴された。その後、WEC とのコミュニケーションを続けながらもブラウザベンダーを取り込み、外堀を埋めながら HTML Living Standard を公開‧管理し続けていた #hatenaintern)*)+

Slide 140

Slide 140 text

ウェブブラウザベンダと WHATWG 仕様 • 主に Google Chrome 陣営は HTML や DOM に関わる API>?の提案を数多く⾏っている • これらは提案者である Google Chrome には取り込まれるが、Apple や Moziila の 反対に会い、仕様としては取り込まれないことも多々ある • Chrome の仕様トラッカー: Chrome Platform Status • Mozilla の⽴場を表明しているウェブサイト: Mozilla Specification Positions • W}C 内の Web Incubator Community Groupというところで様々な提案が作成‧議 論されている !" https://scrapbox.io/pastak-pub/⾯⽩ WebAPI<"" 連発 で⾊々紹介しています #hatenaintern)*)+

Slide 141

Slide 141 text

W"C CSS Specifications • いわゆる CSS) と呼ばれる CSS の現在‧未来の仕様は WHATWG ではなく引き続き W)C によって更新‧管理‧策定が ⾏われている • All CSS specifications #hatenaintern)*)+