Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
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
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
810
ECMAScript仕様の最新動向: プロセスの変化と仕様のトレンド
uhyo
2
660
TypeScript 6.0で非推奨化されるオプションたち
uhyo
17
6.3k
Claude Code 10連ガチャ
uhyo
5
960
AI時代、“平均値”ではいられない
uhyo
8
3.3k
意外と難しいGraphQLのスカラー型
uhyo
5
940
RSCの時代にReactとフレームワークの境界を探る
uhyo
13
4.6k
知られざるprops命名の慣習 アクション編
uhyo
12
3.3k
libsyncrpcってなに?
uhyo
0
740
Other Decks in Technology
See All in Technology
20251218_AIを活用した開発生産性向上の全社的な取り組みの進め方について / How to proceed with company-wide initiatives to improve development productivity using AI
yayoi_dd
0
130
[デモです] NotebookLM で作ったスライドの例
kongmingstrap
0
160
寫了幾年 Code,然後呢?軟體工程師必須重新認識的 DevOps
cheng_wei_chen
1
1.5k
Strands Agents × インタリーブ思考 で変わるAIエージェント設計 / Strands Agents x Interleaved Thinking AI Agents
takanorig
3
290
Database イノベーショントークを振り返る/reinvent-2025-database-innovation-talk-recap
emiki
0
230
Power of Kiro : あなたの㌔はパワステ搭載ですか?
r3_yamauchi
PRO
0
180
Microsoft Agent 365 についてゆっくりじっくり理解する!
skmkzyk
0
380
Identity Management for Agentic AI 解説
fujie
0
110
AWS運用を効率化する!AWS Organizationsを軸にした一元管理の実践/nikkei-tech-talk-202512
nikkei_engineer_recruiting
0
110
Strands AgentsとNova 2 SonicでS2Sを実践してみた
yama3133
0
290
品質のための共通認識
kakehashi
PRO
4
370
打 造 A I 驅 動 的 G i t H u b ⾃ 動 化 ⼯ 作 流 程
appleboy
0
360
Featured
See All Featured
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
390
Bash Introduction
62gerente
615
210k
How to make the Groovebox
asonas
2
1.8k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
37
2.7k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Visualization
eitanlees
150
16k
A Modern Web Designer's Workflow
chriscoyier
698
190k
Color Theory Basics | Prateek | Gurzu
gurzu
0
140
Tell your own story through comics
letsgokoyo
0
740
Scaling GitHub
holman
464
140k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Speed Design
sergeychernyshev
33
1.4k
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