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.8k
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アップデートのために必要なこと
uhyo
5
880
color-scheme: light dark; を完全に理解する
uhyo
7
470
React 19 + Jotaiを試して気づいた注意点
uhyo
9
2.9k
TypeScriptの次なる大進化なるか!? 条件型を返り値とする関数の型推論
uhyo
3
2.9k
tsconfig.jsonの最近の新機能 ファイルパス編
uhyo
8
3.3k
非同期処理を活用しながらRust製wasmとJSを連携する方法(wasm-bindgenを使いたくない人向け)
uhyo
4
4.1k
tsconfig.jsonの設定を見直そう!フロントエンド向け 2024夏
uhyo
26
10k
React 19を概念から理解する
uhyo
22
10k
require(ESM)とECMAScript仕様
uhyo
7
2.2k
Other Decks in Technology
See All in Technology
オブザーバビリティの観点でみるAWS / AWS from observability perspective
ymotongpoo
9
1.8k
AI Agent時代なのでAWSのLLMs.txtが欲しい!
watany
2
130
ローカルLLMを活用したコード生成と、ローコード開発ツールへの応用
kazuhitoyokoi
0
140
Autonomous Database Serverless 技術詳細 / adb-s_technical_detail_jp
oracle4engineer
PRO
17
45k
プロダクトエンジニア 360°フィードバックを実施した話
hacomono
PRO
0
130
ExaDB-XSで利用されているExadata Exascaleについて
oracle4engineer
PRO
2
110
一度 Expo の採用を断念したけど、 再度 Expo の導入を検討している話
ichiki1023
1
250
【5分でわかる】セーフィー エンジニア向け会社紹介
safie_recruit
0
18k
株式会社EventHub・エンジニア採用資料
eventhub
0
4.3k
次世代KYC活動報告 / 20250219-BizDay17-KYC-nextgen
oidfj
0
450
脳波を用いた嗜好マッチングシステム
hokkey621
0
250
【詳説】コンテンツ配信 システムの複数機能 基盤への拡張
hatena
0
170
Featured
See All Featured
A Philosophy of Restraint
colly
203
16k
Reflections from 52 weeks, 52 projects
jeffersonlam
348
20k
Product Roadmaps are Hard
iamctodd
PRO
50
11k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
46
2.3k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Embracing the Ebb and Flow
colly
84
4.6k
Designing Experiences People Love
moore
140
23k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
33
2.8k
Six Lessons from altMBA
skipperchong
27
3.6k
YesSQL, Process and Tooling at Scale
rocio
172
14k
A designer walks into a library…
pauljervisheath
205
24k
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