---
title: なぜ型にこだわるか TSKaigi Sendai 2026
tags: 
author: [yuta-ike](https://www.docswell.com/user/4136989)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/4EQDXX1RJP.jpg?width=480
description: スライド中で参照している過去の登壇資料  TSKaigi 2026 「AI時代に考える、Branded Typesで実現する堅牢な型付け https://www.docswell.com/s/4136989/Z6NJ78-tskaigi2026/1  制約を表現する型を書く https://www.docswell.com/s/4136989/KN79JV-tskaigi-side-event-20260609/1
published: November 01, 26
canonical: https://www.docswell.com/s/4136989/K3JVLL-tskaigi-sendai-2026
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/4EQDXX1RJP.jpg)

なぜ型にこだわるのか
2026.11.01 TSKaigi Sendai 2026
@yuta-ike
エムスリー / エンジニアリンググループ

# Page. 2

![Page Image](https://bcdn.docswell.com/page/KJ4Z66RQ71.jpg)

制御フロー解析

# Page. 3

![Page Image](https://bcdn.docswell.com/page/LE1RVVQLEG.jpg)

制御フロー解析による型の絞り込み
分岐やReturnなどの制御の流れに応じて、変数の型を絞り込む。
function format(value: string | number) {
if(typeof value === &quot;string&quot;) {
return value.toUpperCase(); // value: string
}
return value.toFixed(2); // value: number
}

# Page. 4

![Page Image](https://bcdn.docswell.com/page/GEW1LLMQ72.jpg)

制御フロー解析による型の絞り込み
分岐やReturnなどの制御の流れに応じて、変数の型を絞り込む。
function format(value: string | number) {
if(typeof value === &quot;string&quot;) {
return value.toUpperCase(); // value: string
}
return value.toFixed(2); // value: number
}

# Page. 5

![Page Image](https://bcdn.docswell.com/page/47ZPMMQ3E3.jpg)

制御フロー解析による型の絞り込み
分岐やReturnなどの制御の流れに応じて、変数の型を絞り込む。
function format(value: string | number) {
if(typeof value === &quot;string&quot;) {
return value.toUpperCase(); // value: string
}
return value.toFixed(2); // value: number
}

# Page. 6

![Page Image](https://bcdn.docswell.com/page/YJ6MNN6Z7V.jpg)

TypeScriptのif文
function format(value: string | number) {
if(typeof value === &quot;string&quot;) {
return value.toUpperCase(); // value: string
}
return value.toFixed(2); // value: number
}
型の絞り込みをするif
通常のif

# Page. 7

![Page Image](https://bcdn.docswell.com/page/GJ5Z5569E4.jpg)

今回のお題
管理者だけがアプリ全体の設定を変更できる、という仕様を考える
type User = AdminUser | NormalUser
function updateConfig(actor: User) {
if (actor.role === &quot;admin&quot;) {
throw new Error(&quot;権限がありません&quot;);
}
// アプリ全体の設定を変更する
}

# Page. 8

![Page Image](https://bcdn.docswell.com/page/LE39NNQ2E5.jpg)

今回のお題
管理者だけがアプリ全体の設定を変更できる、という仕様を考える
type User = AdminUser | NormalUser
function updateConfig(actor: User) {
if (actor.role === &quot;admin&quot;) {
throw new Error(&quot;権限がありません&quot;);
}
// アプリ全体の設定を変更する
}

# Page. 9

![Page Image](https://bcdn.docswell.com/page/8EDGWW96EG.jpg)

今回のお題
管理者だけがアプリ全体の設定を変更できる、という仕様を考える
type User = AdminUser | NormalUser
function updateConfig(actor: User) {
if (actor.role === &quot;admin&quot;) {
throw new Error(&quot;権限がありません&quot;);
}
// アプリ全体の設定を変更する
}

# Page. 10

![Page Image](https://bcdn.docswell.com/page/V7P3999ZJ8.jpg)

今回のお題
管理者だけがアプリ全体の設定を変更できる、という仕様を考える
type User = AdminUser | NormalUser
function updateConfig(actor: User) {
if (actor.role === &quot;admin&quot;) {
throw new Error(&quot;権限がありません&quot;);
}
// アプリ全体の設定を変更する
}

# Page. 11

![Page Image](https://bcdn.docswell.com/page/2JV4MMMM7Q.jpg)

今回のお題
管理者だけがアプリ全体の設定を変更できる、という仕様を考える
type User = AdminUser | NormalUser
function updateConfig(actor: User) {
if (actor.role === &quot;admin&quot;) {
throw new Error(&quot;権限がありません&quot;);
}
// アプリ全体の設定を変更する
}

# Page. 12

![Page Image](https://bcdn.docswell.com/page/5EG1GGGXJL.jpg)

権限チェックの書き忘れ
権限チェックのif文を書き忘れても型エラーにならない。
function updateConfig(actor: User) {
// if (actor.role === &quot;admin&quot;) {
// throw new Error(&quot;権限がありません&quot;);
// }
// アプリ全体の設定を変更する
}
const actor: User = { id: &quot;1&quot;, role: &quot;Normal&quot; };
updateConfig(actor); // 型エラー無し

# Page. 13

![Page Image](https://bcdn.docswell.com/page/4JQDXXX57P.jpg)

権限チェックの書き忘れ
権限チェックのif文を書き忘れても型エラーにならない。
function updateConfig(actor: User) {
// if (actor.role === &quot;admin&quot;) {
// throw new Error(&quot;権限がありません&quot;);
// }
// アプリ全体の設定を変更する
}
const actor: User = { id: &quot;1&quot;, role: &quot;Normal&quot; };
updateConfig(actor); // 型エラー無し

# Page. 14

![Page Image](https://bcdn.docswell.com/page/K74Z666VE1.jpg)

せっかくTypeScriptを使うのであれば、型エラーが出てほしい...

# Page. 15

![Page Image](https://bcdn.docswell.com/page/LJ1RVVV4JG.jpg)

型の絞り込みに着目
if文は型の絞り込みをしているものの、絞り込まれたことを検知するロジックはない
type User = AdminUser | NormalUser
function updateConfig(actor: User) {
if (actor.role === &quot;admin&quot;) {
throw new Error(&quot;権限がありません&quot;);
}
// アプリ全体の設定を変更する
}

# Page. 16

![Page Image](https://bcdn.docswell.com/page/GJW1LLLZE2.jpg)

型の絞り込みに着目
if文は型の絞り込みをしているものの、絞り込まれたことを検知するロジックはない
type User = AdminUser | NormalUser
function updateConfig(actor: User) {
if (actor.role === &quot;admin&quot;) {
throw new Error(&quot;権限がありません&quot;);
}
// アプリ全体の設定を変更する
}

# Page. 17

![Page Image](https://bcdn.docswell.com/page/4EZPMMMLJ3.jpg)

型が絞り込まれていることが検証できるようにすると良さそう

# Page. 18

![Page Image](https://bcdn.docswell.com/page/Y76MNNNMEV.jpg)

リファクタ
function updateConfig(actor: AdminUser) {
// アプリ全体の設定を変更する
}
function handler(actor: User) {
if(actor.role === &quot;admin&quot;) {
updateConfig(actor) // const actor: AdminUser
}
}

# Page. 19

![Page Image](https://bcdn.docswell.com/page/G75Z555QJ4.jpg)

リファクタ
function updateConfig(actor: AdminUser) {
// アプリ全体の設定を変更する
}
function handler(actor: User) {
if(actor.role === &quot;admin&quot;) {
updateConfig(actor) // const actor: AdminUser
}
}

# Page. 20

![Page Image](https://bcdn.docswell.com/page/9J2RNNNW7R.jpg)

リファクタ
function updateConfig(actor: AdminUser) {
// アプリ全体の設定を変更する
}
function handler(actor: User) {
if(actor.role === &quot;admin&quot;) {
updateConfig(actor) // const actor: AdminUser
}
}

# Page. 21

![Page Image](https://bcdn.docswell.com/page/DEYDPPP9EM.jpg)

if文の漏れが検知できる
if文を書き忘れると...型エラーになる（！！）
function updateConfig(actor: AdminUser) {
// アプリ全体の設定を変更する
}
function handler(actor: User) {
// if(actor.role === &quot;admin&quot;) {
updateConfig(actor) // const actor: User
// }
// Argument of type &#039;User&#039; is not
assignable to parameter of type &#039;AdminUser&#039;.
}

# Page. 22

![Page Image](https://bcdn.docswell.com/page/VJN6555DJ8.jpg)

考察
一見等価なロジックなのに、
なぜ型エラーの有無が変わるのか？

# Page. 23

![Page Image](https://bcdn.docswell.com/page/YE9LYYY873.jpg)

考察
一見等価なロジックなのに、
なぜ型エラーの有無が変わるのか？
function updateConfig(actor: User) {
if (actor.role === &quot;admin&quot;) {
throw new Error(&quot;権限がありません&quot;);
}
// アプリ全体の設定を変更する
}
function handler(actor: User) {
updateConfig(actor)
}
リファクタ前
function updateConfig(actor: AdminUser) {
// アプリ全体の設定を変更する
}
function handler(actor: User) {
if(actor.role === &quot;admin&quot;) {
updateConfig(actor)
}
}
リファクタ後

# Page. 24

![Page Image](https://bcdn.docswell.com/page/GE8X666ZJD.jpg)

if文が型解析に影響を与える
リファクタ後のコード
function updateConfig(actor: AdminUser) {
// アプリ全体の設定を変更する
}
function handler(actor: User) {
if(actor.role === &quot;admin&quot;) {
updateConfig(actor) // const actor: AdminUser
}
}

# Page. 25

![Page Image](https://bcdn.docswell.com/page/LEL899917R.jpg)

if文が型解析に影響を与える
リファクタ後のコード
function updateConfig(actor: AdminUser) {
// アプリ全体の設定を変更する
}
function handler(actor: User) {
if(actor.role === &quot;admin&quot;) {
updateConfig(actor) // const actor: AdminUser
}
}

# Page. 26

![Page Image](https://bcdn.docswell.com/page/4JM6ZZZ5EW.jpg)

if文が型解析に影響を与える
リファクタ後のコード
function updateConfig(actor: AdminUser) {
// アプリ全体の設定を変更する
}
function handler(actor: User) {
if(actor.role === &quot;admin&quot;) {
updateConfig(actor) // const actor: AdminUser
}
}

# Page. 27

![Page Image](https://bcdn.docswell.com/page/PJRP666ZE9.jpg)

型の絞り込みが発生するif文か
• if文が型の絞り込みの役割を果たしている
• updateConfigが絞り込み後の型しか受け取らない
→ if文の漏れを型レベルで検知できた
→ if文が型レベルでちゃんと仕事をしている状態

# Page. 28

![Page Image](https://bcdn.docswell.com/page/PEX3MMM1JX.jpg)

最初からリファクタ後の実装を出すにはどう考えれば良いか？

# Page. 29

![Page Image](https://bcdn.docswell.com/page/3EKYGGGMJD.jpg)

さらに考察
インターフェースに着目する

# Page. 30

![Page Image](https://bcdn.docswell.com/page/L739NNN275.jpg)

リファクタ前後のインターフェースを考える
リファクタ前
function updateConfig(actor: User): Promise&lt;void&gt;
リファクタ後
function updateConfig(actor: AdminUser): Promise&lt;void&gt;

# Page. 31

![Page Image](https://bcdn.docswell.com/page/87DGWWW67G.jpg)

リファクタ前後のインターフェースを考える
リファクタ前
function updateConfig(actor: User): Promise&lt;void&gt;
リファクタ後
function updateConfig(actor: AdminUser): Promise&lt;void&gt;
→ リファクタ後の方が、「管理者しか変更できない」という
&quot;意図&quot;をよりよく表現している

# Page. 32

![Page Image](https://bcdn.docswell.com/page/VJP399XZE8.jpg)

「意図の明白なインターフェース」
関数の名前やシグネチャから、その関数の目的や振る舞いが明白に分かること。

# Page. 33

![Page Image](https://bcdn.docswell.com/page/2EV4MMLMJQ.jpg)

意図とは何か？
プログラムの意図 = アプリケーションの仕様

# Page. 34

![Page Image](https://bcdn.docswell.com/page/57G1GGXXEL.jpg)

プログラマの仕事
アプリケーションの仕様
翻訳
TypeScript（ドメインロジック）

# Page. 35

![Page Image](https://bcdn.docswell.com/page/4EQDXX85JP.jpg)

意図が明示的であることのメリット
• 可読性が高まる
• 実装の中身を読まなくても関数の処理を把握できる（適切なカプセル化）
TypeScriptでは、、、

# Page. 36

![Page Image](https://bcdn.docswell.com/page/KJ4Z662V71.jpg)

意図が明示的であることのメリット
• 可読性が高まる
• 実装の中身を読まなくても関数の処理を把握できる
TypeScriptでは、、、
• 意図（= 仕様）を型で表現することで、我々が書いた
実装が仕様を満たすかのチェックをTypeScriptに委譲
することができる

# Page. 37

![Page Image](https://bcdn.docswell.com/page/LE1RVVM4EG.jpg)

意図が明示的であることのメリット
「updateConfigという操作は管理者しか呼び出せない」という仕様を型で表
現することで、updateConfigを管理者以外のユーザーが呼び出すコードを型検
査でエラーにできる。
function updateConfig(actor: AdminUser): Promise&lt;void&gt; {
// ...
}
const user = { id: &quot;1&quot;, role: &quot;Normal&quot; }
updateConfig(user)
// Argument of type &#039;User&#039; is not assignable
to parameter of type &#039;AdminUser&#039;.

# Page. 38

![Page Image](https://bcdn.docswell.com/page/GEW1LL3Z72.jpg)

仕様を型に&quot;エンコード&quot;する
仕様：「updateConfigという操作は管理者しか呼び出せない」
function updateConfig(actor: AdminUser): Promise&lt;void&gt;

# Page. 39

![Page Image](https://bcdn.docswell.com/page/47ZPMMVLE3.jpg)

なぜ型で担保したい？
• 他にも色々な方法はある
• 自動テスト
• QA
• 動作確認
→ これらは、高々N個のケースしか網羅できない
（テストケースやQAシナリオの正確性に依存する）
一方で、型は任意の値に対して成立することを保証できる
※完璧ではないけど

# Page. 40

![Page Image](https://bcdn.docswell.com/page/YJ6MNN5M7V.jpg)

なぜ複雑な型を書くのか？
仕様を過不足なく型で表現するために必要だから。
そして、仕様を型で表現することで、実装が仕様を満たして
いることの保証（の一部）をTypeScriptコンパイラに委譲
できるから。

# Page. 41

![Page Image](https://bcdn.docswell.com/page/GJ5Z55YQE4.jpg)

まとめ
• 型は単なるデータの型だけでなく、仕様を表現するための
手段になる
• 良い型を書くことで、実装が仕様を満たしているかを
TypeScriptにチェックさせることが可能。
• 型で保証することで、自動テストやQAが苦手な側面（網
羅性）をカバーすることができる

# Page. 42

![Page Image](https://bcdn.docswell.com/page/9E2RNNGWJR.jpg)

まとめ
• 型は単なるデータの型だけでなく、仕様を表現するための
手段になる
• 良い型を書くことで、実装が仕様を満たしているかを
TypeScriptにチェックさせることが可能。
• 型で保証することで、自動テストやQAが苦手な側面（網
羅性）をカバーすることができる
→ 型を味方につけよう

# Page. 43

![Page Image](https://bcdn.docswell.com/page/D7YDPPV97M.jpg)

補足：型とユニットテスト
User型
AdminUser型
updateConfigが
エラーをthrowする
updateConfigが正常に終了する

# Page. 44

![Page Image](https://bcdn.docswell.com/page/VEN655MDE8.jpg)

補足：型とユニットテスト
そのほかの任意の値
User型
AdminUser型
updateConfigが
型エラーになる
updateConfigが
エラーをthrowする
updateConfigが正常に終了する

# Page. 45

![Page Image](https://bcdn.docswell.com/page/Y79LYYN8J3.jpg)

補足：型とユニットテスト
updateConfigの成功の定義（事後条件）と、その前提条件（事前条件）
事前条件
actorが
AdminUserである
...が成立するならば...
事後条件
設定変更の処理が
正常に完了する

# Page. 46

![Page Image](https://bcdn.docswell.com/page/G78X66KZED.jpg)

補足：型とユニットテスト
updateConfigの成功の定義（事後条件）と、その前提条件（事前条件）
事前条件
actorが
AdminUserである
...が成立するならば...
事後条件
設定変更の処理が
正常に完了する
事前条件が成立していることを、どうやって保証するか？

# Page. 47

![Page Image](https://bcdn.docswell.com/page/L7L899Z1JR.jpg)

補足：型とユニットテスト
リファクタ前 function updateConfig(actor: User): Promise&lt;void&gt;
ok
Unit Test で弾く
型で弾く
AdminUser
User
その他の値

# Page. 48

![Page Image](https://bcdn.docswell.com/page/4EM6ZZV57W.jpg)

補足：型とユニットテスト
リファクタ前 function updateConfig(actor: User): Promise&lt;void&gt;
ok
Unit Test で弾く
型で弾く
AdminUser
User
その他の値
ok
型で弾く
リファクタ後 function updateConfig(actor: AdminUser): Promise&lt;void&gt;

# Page. 49

![Page Image](https://bcdn.docswell.com/page/PERP662Z79.jpg)

補足：型とユニットテスト
型の表現力を高めることで、ユニットテストで保証する範囲を狭めることが
できる。
• リファクタ前の実装では、NormalUserを受け付けないことをランタイム
のテストでチェックする必要があった
const user = { id: &quot;1&quot;, role: &quot;normal&quot; }
expect(() =&gt; updateConfig(user)).toThrowError()
• リファクタ後は、そもそもNormalUserを引数に渡せなくなった。
• そのようなコードを書いた時点で型エラーになる（シフトレフト）
function updateConfig(actor: AdminUser): Promise&lt;void&gt;

# Page. 50

![Page Image](https://bcdn.docswell.com/page/P7X3MMD1EX.jpg)

蛇足①
とはいえ、世の中の仕様に対して型の表現力は非常に小さいので、
うまく型で表せないものは多数ある。
• 「0以上の整数」を表す型 → TSでは表現できない
• 「空ではない文字列」を表す型 → TSでは表現できない
• 「validなメールアドレス」を表す型 → めっちゃ頑張れば可能？

# Page. 51

![Page Image](https://bcdn.docswell.com/page/37KYGGXMED.jpg)

蛇足①
Branded Typesを使うと、型の自作のようなことが可能。これにより任意の
制約を表現できる。（ただしランタイム処理に依存する）
type PositiveInt = number &amp; { __brand: &quot;positive&quot; }
const PositiveInt = (val: number) =&gt; {
if(0 &lt; val &amp;&amp; Number.isInteger(val)) {
return val as PositiveInt
}
throw new Error(&quot;条件を満たしません&quot;)
}

# Page. 52

![Page Image](https://bcdn.docswell.com/page/LJ39NN52J5.jpg)

詳しくはこちら
TSKaigi 2026
Leveragesトラック 30分セッション DAY2 15:10 ~ 15:40
AI時代に考える
Branded Typesで実現する堅牢な型付け
池奥裕太/@yuta-ike
https://www.docswell.com/s/4136989/Z6NJ78-tskaigi2026/1

# Page. 53

![Page Image](https://bcdn.docswell.com/page/8JDGWWD6JG.jpg)

蛇足②
引数の型を「User → AdminUser」に変更することで、実はthrow Errorを消
すことができている。
function updateConfig(actor: User) {
if (actor.role === &quot;admin&quot;) {
throw new Error(&quot;権限がありません&quot;);
}
// アプリ全体の設定を変更する
}
function updateConfig(actor: AdminUser) {
// アプリ全体の設定を変更する
}
function handler(actor: User) {
if(actor.role === &quot;admin&quot;) {
updateConfig(actor)
}
}

# Page. 54

![Page Image](https://bcdn.docswell.com/page/VEP3996Z78.jpg)

蛇足②
エラーのthrowは型に表出しないため、「明白なインターフェース」からは遠
ざかる。
理想はエラーをthrowしない関数 =&gt; 全域関数

# Page. 55

![Page Image](https://bcdn.docswell.com/page/27V4MM3MEQ.jpg)

全域関数と部分関数
全域関数: 任意の入力について答えが定まる関数
（必ずreturnする）
部分関数: 答えが定まらない入力が存在する関数
（Errorが出る入力パターンがある）
ex. 足し算はオーバーフローを考慮しなければ全域関数
割り算はN÷0が定義されないので部分関数

# Page. 56

![Page Image](https://bcdn.docswell.com/page/5JG1GG3X7L.jpg)

リファクタ前後のインターフェースを考える
User型
AdminUser型
updateConfigを実行可能
updateConfigを実行できない

# Page. 57

![Page Image](https://bcdn.docswell.com/page/47QDXX55EP.jpg)

リファクタ前後のインターフェースを考える
User型
AdminUser型
元の実装は、User全体を
引数として取っていた
function updateConfig(actor: User): Promise&lt;void&gt;

# Page. 58

![Page Image](https://bcdn.docswell.com/page/KE4Z669VJ1.jpg)

リファクタ前後のインターフェースを考える
User型
AdminUser型
リファクタ後は、確実に処
理を実行できる範囲に引数
を狭めている
function updateConfig(actor: AdminUser): Promise&lt;void&gt;

# Page. 59

![Page Image](https://bcdn.docswell.com/page/L71RVVK47G.jpg)

リファクタ前後のインターフェースを考える
• 処理が定義できない値を入力範囲（定義域）から外すことで、関数内部で発生
する異常系を潰すことができる
User型
AdminUser型
updateConfigを実行可能
updateConfigを実行できない

# Page. 60

![Page Image](https://bcdn.docswell.com/page/G7W1LLDZJ2.jpg)

定義域を狭めても発生するエラーの対処
DBの接続エラーなど、アプリケーションの外部に由来するエラーは排除できない
• 汎化して言うと、入力値（引数）以外に依存して引き起こされるエラーは排除
できない
→ そのようなエラーが発生し得る環境でも全域化を行う手段がResult型
→ 入力範囲（定義域）を狭めるのではなく、戻り値の範囲（値域）を広げる。

# Page. 61

![Page Image](https://bcdn.docswell.com/page/4JZPMMGL73.jpg)

Result型も同じアプローチ
エラーの代わりに値を返すことで、エラー情報を型に表出させる
async function getUserByUserId(userId: string)
: Result&lt;User, UserNotFoundError&gt; {
const user = await db.user.findUnique({ id: userId })
if(user == null) {
return err(new UserNotFoundError(&quot;User not found&quot;))
}
return ok(user)
}

# Page. 62

![Page Image](https://bcdn.docswell.com/page/YE6MNNYMJV.jpg)

Resultを使う前に、引数を絞れないか考える
例えばこのように、PermissionErrorをResult型で返すのは悪手。
function updateConfig(actor: User)
: Result&lt;void, PermissionError&gt;{
if (actor.role === &quot;admin&quot;) {
return err(new PermissionError())
}
// アプリ全体の設定を変更する
}

# Page. 63

![Page Image](https://bcdn.docswell.com/page/GE5Z55GQ74.jpg)

Resultを使う前に、引数を絞れないか考える
なぜなら、PermissionErrorが起きえない入力でもResultが帰ってしまうため。
const actor: AdminUser = { id: &quot;1&quot;, role: &quot;admin&quot; }
const res = updateConfig(actor)
// ^ const res: Result&lt;void, PermissionError&gt;
if(!res.ok) console.error(&quot;権限エラーが発生しました&quot;)
// ^ 絶対にtrueにならないifを書く羽目に...
updateConfigに、AdminUserを渡せば処理は必ず成功するはず。
しかしResult型が返ってしまうためエラーハンドリングをしなければならない。
→ 型の誤魔化しや虚無のエラーハンドリングを行う羽目になる

# Page. 64

![Page Image](https://bcdn.docswell.com/page/972RNNYWER.jpg)

なぜこうなるのか？
理論上、updateConfigの出力の型は入力の型に依存して変化する。
しかし現状のTypeScriptはこの絞り込みをサポートしていない。
NormalUser → 必ずError
入力
AdminUser → 必ずSuccess
出力
User
(AdminUser | NormalUser) → Success | Error

# Page. 65

![Page Image](https://bcdn.docswell.com/page/DJYDPPG9JM.jpg)

FYI: 迂回策①
オーバーロードを利用する。
function updateConfig(actor: AdminUser): Result&lt;Config, never&gt;
function updateConfig(actor: NormalUser): Result&lt;never, PermissionError&gt;
function updateConfig(actor: User): Result&lt;Config, PermissionError&gt;
function updateConfig(actor: User): Result&lt;Config, PermissionError&gt; {
// ...
return { ok: true, data: { color: &quot;green&quot; } }
}
playground

# Page. 66

![Page Image](https://bcdn.docswell.com/page/V7N655GD78.jpg)

FYI: 迂回策②
Conditional Typeで型レベルで条件分岐を表現する
function updateConfig&lt;T extends User&gt;(actor: T):
T[&quot;role&quot;] extends &quot;admin&quot;
? Result&lt;Config, never&gt;
: T[&quot;role&quot;] extends &quot;normal&quot;
? Result&lt;never, PermissionError&gt;
: Result&lt;Config, PermissionError&gt; {
// ...
}
playground

# Page. 67

![Page Image](https://bcdn.docswell.com/page/YJ9LYYW8E3.jpg)

FYI: 迂回策
迂回策①：オーバーロードを利用する
迂回策②：型レベルで条件分岐を表現する
どちらもシンプルさには欠ける。ここまでやるのであれば、
素直に引数の型をしぼるべき（と思います）

# Page. 68

![Page Image](https://bcdn.docswell.com/page/GJ8X66NZ7D.jpg)

詳しくはこちら
型の力を引き出す
TypeScriptの書き方
2026.06.09 TSKaigi 2026 本編で話せなかったこと、
話し足りなかったこと
@yuta-ike
エムスリー / エンジニアリンググループ
https://www.docswell.com/s/4136989/KN79JV-tskaigi-side-event-20260609/1

# Page. 69

![Page Image](https://bcdn.docswell.com/page/LJL899P1ER.jpg)

Appendix
元の実装でも、ifの漏れを防ぐ仕組みは可能ではある（一応）
type User = AdminUser | NormalUser
function updateConfig(actor: User) {
if (actor.role === &quot;admin&quot;) {
throw new Error(&quot;権限がありません&quot;);
}
const _: AdminUser = actor
}
actorがAdminUser
でない場合は型エラー

