🎄
TypeScript一人カレンダー 2022/2024 目次と索引 記号・A-N
こんにちは、クレスウェア株式会社の奥野賢太郎 (@okunokentaro) です。本記事は『TypeScript 一人 Advent Calendar 2022』および『TypeScript 一人 Advent Calendar 2024』(TypeScript一人カレンダー) 全50記事の目次と索引ページです。
- https://qiita.com/advent-calendar/2022/okunokentaro-ts
- https://qiita.com/advent-calendar/2024/okunokentaro-ts
本アドベントカレンダー内の各記事では、2024年12月、TypeScript 5.7, Next.js 14.x, Valibot 0.42の時点で解説しています。本アドベントカレンダーのすべての記事は、TypeScript初心者を脱し始めてもっと活用していきたいと願う読者層に向けていますので、最低限の前提は省略して記述しています。
目次
シーズン1
第1部 Conditional Types
-
1日目
ReturnType<T>- Utility Types
ReturnType<T>- どんなときに便利?
- 例1
- 例2
- 注意点
-
2日目
Awaited<T>Awaited<T>- どんなときに便利?
- 例1
- 例2
-
3日目
inferと実例UnArray<T>infer-
ReturnType<T>を読んでみる-
extendsがたくさん出てきて難しい?
-
- 実例
UnArray<T>
-
4日目 実例
runRenderHook()- テストでちょっと困る型の扱い
- うーん?テストは書けたけど
-
runRenderHook()- 戻り型アノテーションについて注意
-
5日目
Parameters<T>,ConstructorParameters<T>-
Parameters<T>- Labeled Tuple Elements
- 得た型の使い方
- Indexed Access Types
- どんなときに便利?
ConstructorParameters<T>
-
第2部 Mapped Types
-
6日目
Pick<T, K>-
Pick<T, K>- keyof Type Operator
- どう使う?
- 例1
- 例2
-
-
7日目
Omit<T, K>Omit<T, K>- どう使う?
- 例
-
Pick<T, K>との使い分け
-
8日目
Extract<T, U>とExclude<T, U>-
Extract<T, U>- 業務でよく使う例1
- 業務でよく使う例2
Exclude<T, U>
-
-
9日目 Mapped Typesを活用する
- Mapped Types
- どうやって定義する?
- Template Literal Types
-
Pick<T, K>の中身をみてみる
- Mapped Types
-
10日目
readonlyとReadonlyArray<T>readonly-
readonlyプロパティ-
classの場合の扱い
-
-
readonly配列- 型の書き方
- 予約語ではない
- いつ使う?
- 余談
- Change Array by copy
- Records & Tuples
-
11日目 Mapping Modifiersと実例
Writable<T, K>- テストでちょっと困る型の扱い2
Writable<T, K>
-
12日目
Partial<T>Partial<T>-
Omit<T, K>と異なる点- プロパティ定義なし
- Optionalなプロパティ
-
T | undefinedのUnion
-
HasPropertyについて理解する
-
13日目 実例
RecursivePartial<T>-
Partial<T>を活用する瞬間 -
RecursivePartial<T>- なにをやっているか
- 再帰を活用しよう
-
-
14日目
Required<T>,Readonly<T>Required<T>Readonly<T>- いつ使う?
-
15日目
objectとRecord<K, T>-
Record<K, T>- プロパティとして指定しうる値の型
-
undefinedリスクに注意
- 縁の下の力持ち
- 任意のオブジェクト
- プロパティをひとつも含まないオブジェクト
- 業務ではあまり使わない
-
-
16日目
intrinsicとTemplate Literal Typesintrinsic- Intrinsic String Manipulation Types
- 業務ではどう使う?
- Template Literal Types における infer の挙動
- camelCaseからsnake_caseに変換する型を作る
- TypeScriptはチューリング完全
第3部 Assertion Types
-
17日目
NonNullable<T>と実例assertExists()NonNullable<T>- nullの可能性を除去する実装
- コンポーネント内でNullableな変数を扱うハンドラ
- 雑なnull逃し
- 例外を投げるように変更
- Assertion Functions
assertExists()exists()- コンポーネントのハンドラを短く書く
- 嘘をつけてしまうため注意
-
Array.prototype.filter()でのちょっと便利な書き方
-
18日目 Branded Typesを導入してみる
- Nominal TypingとStructural Typing
- Type Alias
- Nominal Typing Likeなclass
- Branded Types
- Branded Types が解決したこと
-
19日目 実例
FilledString,UserId- Branded Typesの改良
- Type Assertionsが使えるときと、使えないとき
- Type Assertionsが危険である状況
- Assertion Functionsを採用する
-
assertString()とasString() FilledString- アレンジした
Brand<K, T>で宣言するUserId
- Branded Typesの改良
-
20日目 String Literal Typesと
as constenum- String Literal TypesとUnion Types
-
isSuit()関数を実装する- 定数を先に宣言しておくリファクタリング
- 定数から型定義を作りたい
as const
コラム
-
21日目
if文とswitch文は使い分ける?- ちょっとした疑問
- Narrowingとエディタの挙動
- Visual Studio Code
-
if文 -
switch文
-
- WebStorm
-
if文 -
switch文
-
- 結論、やはりどっちでもいい
-
22日目 実例
mapOrElse()- ジェネリクスはどういうときに使うか
mapOrElse()- 身近なジェネリクスを扱う例
-
23日目 実例
extends Error- TypeScriptプログラミングにおけるエラー
- ECMAScript処理系で実行されるという事実からは逃れることができない
- 完璧なフォローはコストが高い
-
catchブロック内での型付け -
catchのerrはError型インスタンスとは限らない - 暗黙的に
err: Errorであると扱えない理由 - 実例
extends Error- Errorを継承しないクラスはおすすめしない
-
24日目 実例 再帰型定義と
RGBA- 過去の頑張りを話します
- TypeScriptの限界
-
RGBA改 - TypeScript 5.0
第4部 Conditional Types, Mapped Types, Assertion Typesを組み合わせる
-
25日目 実例
assertMatchedType()- TypeScriptの集大成
- 不確実な環境での安心の獲得
- 不整合は常に起こりうる
- 防御的プログラミングと契約による設計
- 不整合はなぜ起こったか
- 内部起因か外部起因か
-
unknown型を扱った信頼できる仕組みづくり- Assertion Functions
-
unknown型を採用する欠点 - 開発のモチベーション
-
assertMatchedType()の実装- 型のユニットテスト
- 登り方を検討する
RemoveNeverProperties<T>Escape<T>UnionToUnionTuple<T>-
isMatchedType<T>()のランタイム実装 assertMatchedType()
- 完成
- 総括
シーズン2
第1部 Valibot を前提とした実装の変化
-
1日目 Valibotのご紹介
- Valibot
- Valibot と Zod のコード比較
- Valibot を使ってバリデーション処理を実行する
-
2日目 Valibotで実践するBranded types
- Branded typesとは
- Branded typesの浸透とValibot
-
3日目 応用編 Valid branded types
- さらに一歩踏み込むValid branded types
-
FilledString型をValibotで実現する - 1文字以上であることが必須な応用型の定義
- 時間型もより厳密に
- 2年で変わったTypeScriptエコシステム
-
- さらに一歩踏み込むValid branded types
-
4日目 文字列や配列の最大長が決まっていないときの対策
- スキーマ記述と不明確な上限値の問題
- 業務上「最大値未定」が頻出する理由
- 「最大値未定」を明示するアプローチ
- なぜECMAScriptの限界値を参考にするのか
- まとめ
-
5日目 実例
neverUsed(),$()- Valibot利用上のさらなる工夫
-
neverUsed(): 使用しないプロパティを明示する -
$():strictObject()のエイリアスで可読性向上 - エイリアス導入の是非
第2部 ユーティリティ集から学ぶ
-
6日目
DeepReadonly<T>-
DeepReadonlyを求める理由 - ts-essentialsの
DeepReadonlyを導入する - ライブラリ導入時に考えたいこと
- まとめ
-
-
7日目
StrictOmit<T, K>-
Omit<T, K>の弱点 -
StrictOmit型で厳密なエラーチェック - TypeScript標準ユーティリティの設計意図と実務での選択
-
-
8日目 実例
ExtractKeyOf-
Extractの弱点とキー操作 -
ExtractKeyOf<T, K>を自作 - Rustから学んだ「エラーは学びの源」
- 自作型作成の指針
-
-
9日目 実例
is()- typeプロパティを持つオブジェクトとDiscriminated union
- User-defined Type Guard量産からの卒業
- 汎用的な
is()関数による絞り込み - なぜ
is()が有効なのか - まとめ
-
10日目 実例
hooksTestingTools()-
runRenderHook()につきまとう副作用の取り扱いとトラブル -
hooksTestingToolsFactory()の導入 - 実務発のニーズとそれを解決させる工夫の匙加減
-
-
13日目 実例
mustFind()-
strictNullChecksの便利さと不便さ -
mustFind()の実装例 - オーバーロードでType predicate signatureにも対応する
- テストコードでの確認
- TypeScriptテクニックを実務に活かす
-
-
14日目 Vitest
test-d.tsで複雑な型をテストする- 型定義にもテストを書く時代
- Vitestで型定義をテストする
-「型パズル」と呼ばせないために
第3部 TypeScript の新機能を学ぶ
-
11日目 noUncheckedIndexedAccess
- 配列アクセス時の
undefinedリスクを検出するオプション - 無効時と有効時の挙動を比較
-
undefinedチェックの手間を惜しまない -
as constも併用する - 今すぐオプションの有効化を検討しよう
- 配列アクセス時の
-
12日目 satisfies
-
satisfiesで型適合性を柔軟に示す - 変数型アノテーションと
as constの組み合わせによる歪み -
satisfiesで型を表現 -
satisfiesは難しそう?いや文字数が長いだけ
-
-
20日目 ECMAScript Private Fields
- TypeScriptにおけるprivateとECMAScript Private Fields
- コンパイラ・ランタイムの対応状況と運用
- 実際の業務ではどう使う?
- まとめ
- おまけ サンプルコード全容
-
21日目 using宣言
- ECMAScriptの進化と
using宣言-
using宣言とは? -
finallyとtryだけの組み合わせをシンプル化 - デバッグ用ツールへの導入例
-
usingはリソース破棄だけに使うべきか - 将来への期待
-
- おまけ:計測処理の全容
- ECMAScriptの進化と
第4部 モダンWebアプリケーション開発現場からの実例集
-
15日目 App Router 時代のエラーハンドリング
- 2年前からのエラーハンドリングの変化
- App Routerでのエラーハンドリングの難しさ
-
e instanceofに頼らないハンドリングへの回帰 - RSCからClient componentへのインスタンス渡しの問題
- 早期発見の仕組みのために点検を
- エラーハンドリングは最優先で習得すべきスキル
-
16日目 実例
Result<T, E>- エラーの系統を考察する
- プラットフォームがエラーメッセージを加工する盲点
-
throwからResult<T, E>への転換 -
Result<T, E>型 - さらなる欲求
- 戦略的な割り切り
- 学びと総括
-
17日目 実例
ConvenienceFixture,orDefault()- テストとモックの煩雑さに立ち向かう
- Branded typesとテストモックの両立
-
ConvenienceFixture型とorDefault()関数 -
ConvenienceFixture型の型テスト
-
18日目 実例
PageContext,AllowTransitionFrom- App Routerへの大規模リアーキテクチャ
- 複雑なパラメータを管理する
PageContextという概念を整備- どこで使いたいものか、どこでなら再利用してよいのか
- 複雑な遷移を型レベルで制御
-
AllowTransitionFrom型の実例 - なぜページ名の分岐パラメータを増やさないのか
- 「自由を奪う」ことのメリット
-
- まとめ
-
19日目 実例
UnknownifyDiscriminatedUnion-
PageContextとsubjectパラメータ -
extractPageContext()関数でPageContextを作成 -
InferInput<T>とunknown -
subject以外をすべてunknownに - 型テストの実例
-
any回避とunknownの採用
-
第5部 昨今の開発環境とエコシステムを追いかける
-
22日目 tsx TypeScript Execute
- tsx とは
- Node.jsでTypeScriptを動かす
- CJS, ESMの移行過渡期による混迷と、tsxの登場
- TypeScriptランタイムとしての選択肢
- WinterCGの動き
- それでもtsxがしっくりくる
- tsx とは
-
23日目 tsupでバンドルする
- JavaScriptのモジュール史とバンドル文化
- ESMが正式に実装される流れ
- 2024年現在の様子
- tsupが生まれた背景
- なぜtsupが優れているか
- 省コンフィグ
- tsupの裏側
- まとめ
- JavaScriptのモジュール史とバンドル文化
-
24日目 Biomeを使ったLintとフォーマット
- ESLintというlintツール
- Prettierというフォーマットツール
- Biomeの台頭
- Biomeへの乗り換え手順
- タブインデントへの抵抗感
- 5匹の猿の実験
- 今後のBiomeに期待
-
25日目 今後のTypeScriptに期待すること
- TypeScriptへの期待
- TypeScriptはここ数年でも進化を続けている
- 今後を自由に妄想
-
NonNullable<T>のsugar記法がほしい -
ConstraintOf<T>のような型制約を取り出すユーティリティ型 - 高カインド型 (HKT) も欲しい
- 型のパターンマッチも欲しい
-
- TC39提案の動向
- WASMの進化はどうなるか
- WASMとJSを両方出力する言語が流行る?
- 遥か未来のWebアプリケーション開発
- Webブラウザ界に一波乱あるか
- AIフレンドリーなコード
- TypeScript 一人 Advent Calendar 2024のしめくくり
- TypeScriptへの期待
索引
記号・数字
-
__brand(Branded Types Idiom) /2218,2219,2402 -
--strictNullChecks/ → Strict Null Checks -
-readonly/ → Mapping Modifiers -
!/ → Non-null Assertion Operator -
?:/ → Mapped Types -
?.(オプショナルチェーン演算子) /2411 -
? :/ → Conditional Types -
.cjs(拡張子) /2423 -
.cts(拡張子) /2423 -
.editorconfig/2424 -
.js(拡張子) /2423 -
.mjs(拡張子) /2423 -
.mts(拡張子) /2423 -
.test-d.ts(拡張子) /2414 -
.ts(拡張子) /2422 -
.tsx(拡張子) /2422 -
""(空文字列) /2212,2219 -
"use client"(React) /2415 -
[...arr]/ → Spread Syntax -
[](空配列) /2212 -
[0]/ → Indexed Access Types -
[number]/2205 -
[object Object]/2218 -
[P in K](Mapped Types) /2209 -
[P in keyof T](Mapped Types) /2209 -
@testing-library/react/2204 -
#(Private Fields) /2420 -
+?:/ → Mapping Modifiers -
<T>/ → ジェネリクス -
| null(Nullable) /2206,2217 -
|/ → Union Types -
$()(筆者作、Valibot) /2405 -
${T[P]}(Template Literal Types) /2209 - 5匹の猿の実験 /
2424
A
- Abstract Operations (ECMAScript) /
2212 - Action (Valibot) /
2404 - AI /
2425 - Ajv /
2211,2225 -
AllowTransitionFrom型 (筆者作) /2418 - AltJS /
2423 - Anders Hejlsberg (人名) / → アンダース・ヘルスバーグ(人名)
- Andreas Kling (人名) /
2425 -
any/2203,2204,2206,2210,2211,2215,2219,2223,2225,2407,2408,2419 - App Router (Next.js) /
2415,2416,2418,2419 - Apple Intelligence /
2425 -
Array.prototype.filter()/2217,2425 -
Array.prototype.find()/2413 -
Array.prototype.includes()/2220 -
Array.prototype.map()/2209,2222 -
Array.prototype.reverse()/2210 -
Array.prototype.sort()/2210 -
Array.prototype.toReversed()/2210 -
Array.prototype.toSorted()/2210 -
Array<T>/2203,2222 -
as const/2220,2411,2412 -
as unknown as/2206,2219 -
as(Key Remapping viaas) /2219 -
as(Type Assertions) /2218,2219,2220,2412 -
asFilledString()(筆者作) /2219 - AssemblyScript /
2425 -
assertExists()(筆者作) /2217,2411,2413 - Assertion Functions /
2217,2219,2222,2225 - Assertion Signature /
2217 -
assertMatchedTypes()(筆者作) /2225,2401 -
asserts v is T/ → Assertion Signature -
assertString()(筆者作) /2219 -
asString()(筆者作) /2219 -
Awaited<T>/2202,2203,2213 - axios /
2214
B
- Babel /
2423,2424 -
ban-types(typescript-eslint) /2215 -
beforeEach()/2204,2410 - bikeshed /
2207 - Biome /
2424 - Blink /
2425 -
Boolean(コンストラクタ) /2215 - Bower /
2423 -
brand()(Valibot) /2402,2403,2404 -
Brand<K, T>/2218,2219 - Branded Types /
2218,2219,2402,2403,2406,2417,2418 - Browserify /
2423 - Bun /
2422 -
button(HTML Element) /2205
C
- Call this operator (TC39) /
2425 -
Capitalize<S>/2216 -
catch/2223,2415,2421 - Change Array by copy (TC39) /
2210 - ChatGPT /
2425 - Chris Wanstrath (人名) /
2425 - Chromium /
2422 - CI /
2415 - CJS /
2422,2423 -
class/2205,2210,2218,2218,2223,2415,2421 - Claude /
2425 - Clean Architecture 達人に学ぶソフトウェアの構造と設計 (書籍) /
2418 - CMS /
2208 - COBOL /
2425 - CommonJS / → CJS
-
ComputeRange<N, Result>(引用) /2224 - Conditional Types /
2203,2208,2213,2216,2222,2225 -
constAssertion /2220 -
constructor/2205,2210,2218 -
ConstructorParameters<T>/2205 -
ConvenienceFixture型 (筆者作) /2417 -
convertOrElse()(筆者作) /2222 - Create missing branches (WebStorm) /
2221 - CSS in JS /
2224 - Custom Hook /
2204 - C 言語 /
2221
D
- Dan Abramov (人名) /
2223 -
Date.now()/2403 -
DeepReadonly<T>(ts-essentials) /2406 -
delete演算子 /2211 - Deno /
2223,2422 - Design by Contract /
2225 - Destructuring assignment /
2413 -
DetailedHTMLProps/2205 - Discriminated union /
2208,2409,2413,2416,2418,2419 -
Double<T>(筆者作) /2209 - DRY (Don't repeat yourself) /
2205,2409
E
- ECMAScript /
2209,2219,2220,2221,2222 -
ECMAScript Private Fields/2420 - ECMAScript 仕様書 /
2215,2223,2404 - Either モナド /
2223,2416 - Electron /
2202,2423 - Emotion (CSS in JS) /
2224 -
enum/2220 -
Equals<X, Y>(引用) /2225 -
Err<E>/2223,2416 -
Error/2223,2415 - ES Module / → ESM
- ESLint /
2204,2215,2424 -
eslint-disable-next-line/2204 - ESM /
2422,2423 -
Exclude<T, U>/2208 -
exists()(筆者作) /2217,2410 -
expectTypeOf()(Vitest) /2414 - Explicit Resource Management (TC39) /
2421 -
explicit-function-return-type(typescript-eslint) /2204 -
export/2208 - Express.js /
2420 -
extends(Generic Constraints) /2203,2409 -
extends(Inferring) /2203 -
extends(Subclass) /2203,2223 -
Extract<T, U>/2208,2212,2408,2409 -
ExtractKeyOf<T, K>(筆者作) /2408 -
extractPageContext()(筆者作) /2419
F
-
FilledString(筆者作) /2219,2403,2419 -
finally/2421 - Firestore /
2201,2202,2225 - Flat Config (ESLint) /
2424 - fp-ts /
2223,2416 - Function overloads /
2413
G
- Gecko /
2425 - Gemini /
2425 - Generic Constraints /
2203 - Generics /
2209,2222 -
getServerSideProps()(Next.js) /2418 - GitHub Copilot /
2221,2425 -
globalThis.parseInt()/2205 - GoF (Gang of four) /
2418 - Google /
2425 - Go 言語 /
2423 - GraphQL /
2213,2222,2225
H
- Haskell /
2213,2223,2416 -
HasProperty(ECMAScript) /2212 - Higher Kinded Types / → 高カインド型
-
hooksTestingToolsFactory()(筆者作) /2410 - IDE /
2221
I
-
if文 /2204,2220,2221 - immutable /
2210 -
import/2423 - Indexed Access Types /
2205,2220 -
infer/2203,2205,2213,2216 -
InferInput<T>(Valibot) /2419 -
InferOutput<T>(Valibot) /2401,2402,2406 -
inferred(筆者作) /2204 -
Infinity/2404 -
instanceof演算子 /2217,2223,2415 -
interface/2218 - Intrinsic String Manipulation Types /
2216 -
intrinsic/2216 -
is()(筆者作) /2409 -
Item(筆者作) /2207,2218,2219 -
ItemId(筆者作) /2218,2417 -
Iterable<T>/2203
J
- Java /
2213,2218,2420 - JavaScriptCore /
2422 - Jest /
2204,2206,2207,2403, → Vitest - JOIN (SQL) /
2202 - JSON /
2211,2217 - JSON Schema /
2211 -
JSON.parse()/2407 -
JSON.stringify()/2218
K
L
- Labeled Tuple Elements /
2205 - Ladybird /
2425 - Language Service /
2221 -
let(変数宣言) /2201,2204,2410 -
lib.dom.d.ts/2214 -
lib.es5.d.ts/2203,2216 - Literal Types /
2220,2224 -
looseObject()(Valibot) /2405 -
Lowercase<S>/2216
M
-
Map<K, V>/2215 -
mapOrElse()(筆者作) /2222 - Mapped Types /
2209,2211,2212,2213,2214,2215,2216,2219,2222,2225 - Mapping Modifiers /
2211,2212,2213,2214 -
match(筆者作) /2416 - mattmccutchen (人名) /
2225 -
maxArrayLengthNotSpecified()(筆者作) /2404 -
maxStringLengthNotSpecified()(筆者作) /2404 - Maybe モナド /
2223 - Michal Zalecki (人名) /
2218 - Microsoft Copilot /
2425 -
Millisecond型 (筆者作) /2403 - MoonBit /
2425 - MSW /
2420 -
MswServer(筆者作) /2420 -
mui/2205 -
mustFind()(筆者作) /2413,2414
N
- Narrowing /
2217,2221,2223,2225,2409,2413,2414 -
never/2203,2208,2215,2217 -
neverUsed()(筆者作) /2405 - Next.js /
2206,2218,2411,2415,2416,2418,2419 -
NextApiRequest/2206 -
NextApiResponse/2206 - Node.js /
2422,2423 -
node-sqlite/2202 - Nominal Typing /
2218,2418 - Nominal Typing Like な class /
2218 - Non-null Assertion Operator /
2218 -
NonNullable<T>/2217,2410,2425 -
noUncheckedIndexedAccess/2411 - npm /
2423 - Null- and undefined-aware types /
2217 -
null/2212,2213,2217,2219 -
nullチェック /2217 -
Number.MAX_SAFE_INTEGER/2404 -
Number.POSITIVE_INFINITY/2404 -
Number(コンストラクタ) /2215
Discussion