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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
uhyo
March 28, 2024
Technology
8
2k
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
タグ付きユニオン型を便利に使うテクニックとその注意点
uhyo
2
930
ECMAScript仕様の最新動向: プロセスの変化と仕様のトレンド
uhyo
3
760
TypeScript 6.0で非推奨化されるオプションたち
uhyo
17
6.9k
Claude Code 10連ガチャ
uhyo
5
1k
AI時代、“平均値”ではいられない
uhyo
8
3.8k
意外と難しいGraphQLのスカラー型
uhyo
5
1k
RSCの時代にReactとフレームワークの境界を探る
uhyo
13
4.8k
知られざるprops命名の慣習 アクション編
uhyo
12
3.4k
libsyncrpcってなに?
uhyo
0
790
Other Decks in Technology
See All in Technology
SRE Enabling戦記 - 急成長する組織にSREを浸透させる戦いの歴史
markie1009
0
170
広告の効果検証を題材にした因果推論の精度検証について
zozotech
PRO
0
210
ECS障害を例に学ぶ、インシデント対応に備えたAIエージェントの育て方 / How to develop AI agents for incident response with ECS outage
iselegant
3
320
Context Engineeringの取り組み
nutslove
0
380
20260208_第66回 コンピュータビジョン勉強会
keiichiito1978
0
190
コンテナセキュリティの最新事情 ~ 2026年版 ~
kyohmizu
6
1.6k
OCI Database Management サービス詳細
oracle4engineer
PRO
1
7.4k
Codex 5.3 と Opus 4.6 にコーポレートサイトを作らせてみた / Codex 5.3 vs Opus 4.6
ama_ch
0
200
Bedrock PolicyでAmazon Bedrock Guardrails利用を強制してみた
yuu551
0
260
(技術的には)社内システムもOKなブラウザエージェントを作ってみた!
har1101
0
190
ブロックテーマ、WordPress でウェブサイトをつくるということ / 2026.02.07 Gifu WordPress Meetup
torounit
0
200
AIエージェントに必要なのはデータではなく文脈だった/ai-agent-context-graph-mybest
jonnojun
1
250
Featured
See All Featured
Designing Experiences People Love
moore
144
24k
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
740
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
61k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
2.8k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
380
How GitHub (no longer) Works
holman
316
140k
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
68
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
110
The untapped power of vector embeddings
frankvandijk
1
1.6k
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
170
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
350
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