Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
TypeScript Quiz (Encraft #12 Frontend Quiz Night)
Search
uhyo
March 28, 2024
Technology
8
1.7k
TypeScript Quiz (Encraft #12 Frontend Quiz Night)
2024-03-28 Encraft #12 Frontend Quiz Night
uhyo
March 28, 2024
Tweet
Share
More Decks by uhyo
See All by uhyo
React 19 + Jotaiを試して気づいた注意点
uhyo
8
2.2k
TypeScriptの次なる大進化なるか!? 条件型を返り値とする関数の型推論
uhyo
3
2k
tsconfig.jsonの最近の新機能 ファイルパス編
uhyo
7
2.6k
非同期処理を活用しながらRust製wasmとJSを連携する方法(wasm-bindgenを使いたくない人向け)
uhyo
4
3.8k
tsconfig.jsonの設定を見直そう!フロントエンド向け 2024夏
uhyo
26
9.3k
React 19を概念から理解する
uhyo
22
10k
require(ESM)とECMAScript仕様
uhyo
7
2k
Shadow DOMとCSSの現状
uhyo
11
7.4k
TypeScriptってどんな言語? 言語そのものを知る面白さ
uhyo
16
9k
Other Decks in Technology
See All in Technology
権威ドキュメントで振り返る2024 #年忘れセキュリティ2024
hirotomotaguchi
2
730
私なりのAIのご紹介 [2024年版]
qt_luigi
1
120
多領域インシデントマネジメントへの挑戦:ハードウェアとソフトウェアの融合が生む課題/Challenge to multidisciplinary incident management: Issues created by the fusion of hardware and software
bitkey
PRO
2
100
レンジャーシステムズ | 会社紹介(採用ピッチ)
rssytems
0
150
AWS re:Invent 2024 ふりかえり
kongmingstrap
0
130
10分で学ぶKubernetesコンテナセキュリティ/10min-k8s-container-sec
mochizuki875
3
330
20241214_WACATE2024冬_テスト設計技法をチョット俯瞰してみよう
kzsuzuki
3
440
AWS re:Invent 2024で発表された コードを書く開発者向け機能について
maruto
0
190
プロダクト開発を加速させるためのQA文化の築き方 / How to build QA culture to accelerate product development
mii3king
1
260
ずっと昔に Star をつけたはずの思い出せない GitHub リポジトリを見つけたい!
rokuosan
0
150
社外コミュニティで学び社内に活かす共に学ぶプロジェクトの実践/backlogworld2024
nishiuma
0
260
Microsoft Azure全冠になってみた ~アレを使い倒した者が試験を制す!?~/Obtained all Microsoft Azure certifications Those who use "that" to the full will win the exam! ?
yuj1osm
2
110
Featured
See All Featured
Thoughts on Productivity
jonyablonski
67
4.4k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
330
21k
Art, The Web, and Tiny UX
lynnandtonic
298
20k
The Language of Interfaces
destraynor
154
24k
Imperfection Machines: The Place of Print at Facebook
scottboms
266
13k
Building Better People: How to give real-time feedback that sticks.
wjessup
365
19k
How GitHub (no longer) Works
holman
311
140k
The Art of Programming - Codeland 2020
erikaheidi
53
13k
Rails Girls Zürich Keynote
gr2m
94
13k
Building Flexible Design Systems
yeseniaperezcruz
327
38k
Producing Creativity
orderedlist
PRO
341
39k
Fantastic passwords and where to find them - at NoRuKo
philnash
50
2.9k
Transcript
TypeScript Quiz @uhyo_ 2024-03-28 Encraft #12 Frontend Quiz Night
前提 今回の問題では、TypeScriptバージョンは5.4.3(最新版) コンパイラオプションは { “strict”: true } を前提としています。
出題者紹介 uhyo 株式会社カオナビ フロントエンドエンジニア 最近はTypeScriptリポジトリのissueを ウォッチするのが趣味。
第1問 この関数getHelloOrWorldの返り値の型は “hello” | “world” 型ですが…… function getHelloOrWorld() { if
(Math.random() < 0.5) { return "hello"; } else { return "world"; } }
第1問 では、この関数getHelloの返り値の型はどれ? function getHello() { return "hello"; } 1: string
2: “hello” 3: “hello” & string 4: 型エラー
第1問 では、この関数getHelloの返り値の型はどれ? function getHello() { return "hello"; } 1: string
2: “hello” 3: “hello” & string 4: 型エラー 正解は……
第1問 では、この関数getHelloの返り値の型はどれ? function getHello() { return "hello"; } 1: string
第1問 解説 返り値に表れるリテラル型が1つか2つ以上かによって 挙動が異なる。 // string function getHello() { return "hello";
} // “hello” | “world” function getHelloOrWorld() { if (Math.random() < 0.5) { return "hello"; } else { return "world"; } }
第1問 解説 実装当初から一貫してこの挙動で、理由はPR内で 説明されている。 The issue is that you practically never
want the literal type. After all, why write a function that promises to always return the same value? https://github.com/microsoft/TypeScript/pull/10676#issuecomment-244257359 (要約)常に特定の値を返す関数なんて書かんでしょ
第1問 解説 返り値が1つでもリテラル型のほうがいい場合は as constを使う。 // “hello” function getHello() { return
"hello" as const; }
第1問 解説 ちなみに、ユニオン型の場合も let変数に入れるとstringに なってしまう。 (この辺りの細かい挙動はwidening で調べてみよう) function getHelloOrWorld() { if
(Math.random() < 0.5) { return "hello"; } else { return "world"; } } let str = getHelloOrWorld(); // ^? let str: string
第2問 このコードで、KeysとValuesはど んな型? (選択肢は Keys / Values の形) const values
= { foo: 123, bar: 456, } satisfies Record<string, number>; type Keys = keyof typeof values; type Values = typeof values[Keys]; 1: string / number 2: “foo” | “bar” / number 3: string / 123 | 456 4: “foo” | “bar” / 123 | 456
第2問 このコードで、KeysとValuesはど んな型? (選択肢は Keys / Values の形) const values
= { foo: 123, bar: 456, } satisfies Record<string, number>; type Keys = keyof typeof values; type Values = typeof values[Keys]; 1: string / number 2: “foo” | “bar” / number 3: string / 123 | 456 4: “foo” | “bar” / 123 | 456 正解は……
第2問 このコードで、KeysとValuesはど んな型? (選択肢は Keys / Values の形) const values
= { foo: 123, bar: 456, } satisfies Record<string, number>; type Keys = keyof typeof values; type Values = typeof values[Keys]; 2: “foo” | “bar” / number
第2問 解説 satisfiesの特徴は、式が特定の型に当てはまるかどうかチェック しつつ、型推論の結果を尊重してくれること。 const values = { foo: 123, bar:
456, } satisfies Record<string, number>; type Keys = keyof typeof values; type Values = typeof values[Keys];
第2問 解説 なので実はsatisfiesが 無くても結果が同じ。 const values = { foo: 123, bar:
456, }; type Keys = keyof typeof values; type Values = typeof values[Keys]; valuesの型は { “foo”: number; “bar”: number; } なのでKeysは “foo” | “bar”、 Valuesは number となる。
第2問 解説 このケースでsatisfiesを使う目的は、 オブジェクトのプロパティに数値以外が入るのを防ぐこと。 const values = { foo: 123, bar:
456, baz: “789”, // 型エラー } satisfies Record<string, number>; type Keys = keyof typeof values; type Values = typeof values[Keys];
第2問 解説 このようにsatisfiesではなく型注釈を使うと、 Keysがstringになってしまう。 キーの情報を損ないたくない場合にsatisfiesが便利。 const values: Record<string, number> = {
foo: 123, bar: 456, }; type Keys = keyof typeof values; type Values = typeof values[Keys];
第2問 解説 const values = { foo: 123, bar: 456, }
as const satisfies Record<string, number>; type Keys = keyof typeof values; type Values = typeof values[Keys]; Valuesの型も具体的に欲しい場合は、 このようにas constと併用するとよい。 こうするとValuesは123 | 456 型になる。
第3問 今からお見せする4つのTypeScriptコードのうち、 1つだけ型エラーが発生するものがあります。 どれでしょう?
第3問 type Data1 = string | number; function useData1(data: Data1)
{ if (typeof data === "number") { 0 <= data && data <= 10; } } 1
第3問 type Data2 = { type: "success"; value: number; }
| { type: "error"; error: unknown; } function useData2(data: Data2) { if (data.type === "success") { 0 <= data.value && data.value <= 10; } } 2
function useData3(data: object) { if ("foo" in data && typeof
data.foo === "number") { 0 <= data.foo && data.foo <= 10; } } 3 第3問
第3問 function useData4( data: Record<string, unknown>, key: string, ) {
if (typeof data[key] === "number") { 0 <= data[key] && data[key] <= 10; } } 4
type Data1 = string | number; function useData1(data: Data1) {
if (typeof data === "number") { 0 <= data && data <= 10; } } 1 type Data2 = { type: "success"; value: number; } | { type: "error"; error: unknown; } function useData2(data: Data2) { if (data.type === "success") { 0 <= data.value && data.value <= 10; } } 2 function useData3(data: object) { if ("foo" in data && typeof data.foo === "number") { 0 <= data.foo && data.foo <= 10; } } function useData4( data: Record<string, unknown>, key: string, ) { if (typeof data[key] === "number") { 0 <= data[key] && data[key] <= 10; } } 1 3 4 第3問 型エラーが発生するのはどれ?
type Data1 = string | number; function useData1(data: Data1) {
if (typeof data === "number") { 0 <= data && data <= 10; } } 1 type Data2 = { type: "success"; value: number; } | { type: "error"; error: unknown; } function useData2(data: Data2) { if (data.type === "success") { 0 <= data.value && data.value <= 10; } } 2 function useData3(data: object) { if ("foo" in data && typeof data.foo === "number") { 0 <= data.foo && data.foo <= 10; } } function useData4( data: Record<string, unknown>, key: string, ) { if (typeof data[key] === "number") { 0 <= data[key] && data[key] <= 10; } } 1 3 4 第3問 型エラーが発生 するのはどれ? 正解は……
第3問 型エラーが発生するのは4。 1~3は型の絞り込みがうまくいくが、 4は絞り込みができない。
type Data1 = string | number; function useData1(data: Data1) {
if (typeof data === "number") { 0 <= data && data <= 10; } } 1 type Data2 = { type: "success"; value: number; } | { type: "error"; error: unknown; } function useData2(data: Data2) { if (data.type === "success") { 0 <= data.value && data.value <= 10; } } 2 function useData3(data: object) { if ("foo" in data && typeof data.foo === "number") { 0 <= data.foo && data.foo <= 10; } } 1 3 第3問 解説 1~3は絞り込みが効くパターン。
第3問 解説 実は最近、4も絞り込みできるようにするPRが出た。
第3問 解説 function useData4( data: Record<string, unknown>, key: string, ) {
if (typeof data[key] === "number") { 0 <= data[key] && data[key] <= 10; } } 4 このように data[key] に対する絞り込みは、 keyがただのstring型の場合はできなかった。 次バージョンではCFAが拡張され、できるようになりそう。
第4問 type Not<B extends boolean> = B extends true ?
false : true; type IsString<T> = T extends string ? true : false; type IsNotString<T> = Not<IsString<T>>; type Output = IsNotString<string | number>; このコードで、Output型は1~4のうちどれになる? 1: never 2: false 3: true 4: boolean
第4問 type Not<B extends boolean> = B extends true ?
false : true; type IsString<T> = T extends string ? true : false; type IsNotString<T> = Not<IsString<T>>; type Output = IsNotString<string | number>; このコードで、Output型は1~4のうちどれになる? 1: never 2: false 3: true 4: boolean 正解は……
第4問 type Not<B extends boolean> = B extends true ?
false : true; type IsString<T> = T extends string ? true : false; type IsNotString<T> = Not<IsString<T>>; type Output = IsNotString<string | number>; このコードで、Output型は1~4のうちどれになる? 4: boolean
第4問 解説 type Not<B extends boolean> = B extends true ?
false : true; type IsString<T> = T extends string ? true : false; type IsNotString<T> = Not<IsString<T>>; type Output = IsNotString<string | number>; 条件型 (conditional types) の分配 (distribution) 知ってますか? という知識問題でした。 IsString<string | number> = (string extends string ? true : false) | (number extends string ? true : false) = true | false = boolean 型の計算の流れ①
第4問 解説 type Not<B extends boolean> = B extends true ?
false : true; type IsString<T> = T extends string ? true : false; type IsNotString<T> = Not<IsString<T>>; type Output = IsNotString<string | number>; 条件型 (conditional types) の分配 (distribution) 知ってますか? という知識問題でした。 IsNotString<string | number> = Not<IsString<string | number>> = Not<boolean> = (true extends true ? false : true) | (false extends true ? false : true) = false | true = boolean 型の計算の流れ②
予備
第5問 type Not<B extends boolean> = B extends true ?
false : true; type IsString<T> = T extends string ? true : false; type IsNotString<T> = Not<IsString<T>>; type Output = IsNotString<string | number>; // ^? Output = boolean 第4問のこのコードを、Output が true になるように修正します。 分配が起こらないように直せば修正できます。
第5問 type Not<B extends boolean> = B[] extends true[] ?
false : true; type IsString<T> = T[] extends string[] ? true : false; type IsNotString<T> = Not<IsString<T>>; type Output = IsNotString<string | number>; // ^? Output = true 実は、このように直せば分配が起こらなくなり、修正できます。
第5問 type Not<B extends boolean> = B[] extends true[] ?
false : true; type IsString<T> = T[] extends string[] ? true : false; type IsNotString<T> = Not<Not<Not<IsString<T>>>>; type Output = IsNotString<string | number>; // ^? Output = false しかし、調子に乗ってNotを3重にしたら壊れました。 Notの定義を変えることで、Outputがtrueとなる 正しい挙動に修正してください。
第5問 type Not<B extends boolean> = B[] extends true[] ?
false : true; 修正前 type Not<B extends boolean> = readonly B[] extends readonly true[] ? false : true; 1 type Not<B extends boolean> = [B] extends [true] ? false : true; 2 type Not<B extends boolean> = {v:B} extends {v:true} ? false : true; 3 type Not<B extends boolean> = (()=>B) extends (()=>true) ? false : true; 4
第5問 type Not<B extends boolean> = B[] extends true[] ?
false : true; 修正前 type Not<B extends boolean> = readonly B[] extends readonly true[] ? false : true; 1 type Not<B extends boolean> = [B] extends [true] ? false : true; 2 type Not<B extends boolean> = {v:B} extends {v:true} ? false : true; 3 type Not<B extends boolean> = (()=>B) extends (()=>true) ? false : true; 4 正解は……
第5問 type Not<B extends boolean> = B[] extends true[] ?
false : true; 修正前 type Not<B extends boolean> = [B] extends [true] ? false : true; 2
第5問 解説 type Not<B extends boolean> = [B] extends [true] ?
false : true; 2 修正前と1~4はどれも分配を避けられる書き方。 しかし、2のように[ ] で囲む(タプル型を使う)のが公式に推奨されるやり方で あり、ドキュメントにも書いてある。 https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types この問題の例のように、このやり方は型推論の面で少し優遇される。 参考: https://github.com/microsoft/TypeScript/issues/52068