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.9k
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
libsyncrpcってなに?
uhyo
0
290
Next.jsと状態管理のプラクティス
uhyo
7
5.2k
10ヶ月かけてstyled-components v4からv5にアップデートした話
uhyo
5
600
更新系と状態
uhyo
8
3k
React 19アップデートのために必要なこと
uhyo
8
2.4k
color-scheme: light dark; を完全に理解する
uhyo
8
660
React 19 + Jotaiを試して気づいた注意点
uhyo
9
3.4k
TypeScriptの次なる大進化なるか!? 条件型を返り値とする関数の型推論
uhyo
3
3.2k
tsconfig.jsonの最近の新機能 ファイルパス編
uhyo
8
4.3k
Other Decks in Technology
See All in Technology
Understanding_Thread_Tuning_for_Inference_Servers_of_Deep_Models.pdf
lycorptech_jp
PRO
0
150
なぜ私はいま、ここにいるのか? #もがく中堅デザイナー #プロダクトデザイナー
bengo4com
0
1.3k
Connect 100+を支える技術
kanyamaguc
0
150
MySQL5.6から8.4へ 戦いの記録
kyoshidaxx
1
300
さくらのIaaS基盤のモニタリングとOpenTelemetry/OSC Hokkaido 2025
fujiwara3
2
240
ドメイン特化なCLIPモデルとデータセットの紹介
tattaka
1
450
20250625 Snowflake Summit 2025活用事例 レポート / Nowcast Snowflake Summit 2025 Case Study Report
kkuv
1
370
mrubyと micro-ROSが繋ぐロボットの世界
kishima
2
380
ビギナーであり続ける/beginning
ikuodanaka
1
200
強化されたAmazon Location Serviceによる新機能と開発者体験
dayjournal
3
250
CursorによるPMO業務の代替 / Automating PMO Tasks with Cursor
motoyoshi_kakaku
2
790
OpenHands🤲にContributeしてみた
kotauchisunsun
1
500
Featured
See All Featured
A Modern Web Designer's Workflow
chriscoyier
694
190k
Gamification - CAS2011
davidbonilla
81
5.3k
Reflections from 52 weeks, 52 projects
jeffersonlam
351
20k
Building Better People: How to give real-time feedback that sticks.
wjessup
367
19k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
657
60k
Embracing the Ebb and Flow
colly
86
4.7k
A Tale of Four Properties
chriscoyier
160
23k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.7k
The Art of Programming - Codeland 2020
erikaheidi
54
13k
GraphQLとの向き合い方2022年版
quramy
49
14k
The Language of Interfaces
destraynor
158
25k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
34
5.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