TypeScript 3.7の`asserts x is T`型はどのように危険なのか

TypeScirptの動向を少し熱心に追っている方ならば、8月頭にAnders HejlsbergさんがTypeScriptリポジトリに次のプルリクエストを出したことは記憶に新しいでしょう。

- [Assertions in control flow analysis](https://github.com/microsoft/TypeScript/pull/32695)

これはTypeScript 3.7で導入される予定の機能で、関数の返り値の型宣言において`asserts x is T` （`x`は引数名で`T`は型）という構文を書くことが可能になるというものです。

この機能はたいへん面白いのですが、**誤った使い方をするととても危険**です。そこで、この記事では、`asserts`という新しい型述語[^type_predicate]を正しく使いこなせるように皆さんをガイドします。

[^type_predicate]: `is`というキーワードを使って書かれる物を型述語と呼んでいるようです。*Type predicate*の直訳ですが本当に訳語がこれでいいのかちょっと不安です。

## 3行でまとめると

- `asserts`による宣言はTypeScriptにより正しさがチェックされるわけではありません。
- よって、`asserts`を使う場合安全性を保証する責任はコンパイラではなく我々にあります。
- でも`asserts`はめっちゃ便利なので頑張って安全に使っていきましょう。

ということです。では早速、この機能はどういうものなのか、そしてそれがどう危険なのか、安全に使うにはどうすればいいのかを解説していきます。

## `asserts`型述語の解説

今回の新機能をここでは`asserts`型述語と呼ぶことにします（`asserts`型の述語ではなく`asserts`の型述語です）。この`asserts`型述語は、先ほども述べた通り関数の返り値の型宣言として書くことができます。一言で言えば、これは「**関数が例外を投げずに無事に終了したならば、型述語の条件が満たされる**」という宣言です。
簡単な例はこんな感じです。

```ts
function assertIsNumber(x: unknown): asserts x is number {
  if (typeof x !== "number") {
    throw new Error("BOOM");
  }
}
```

この`assertIsNumber`は、「この関数が`throw`しなければ引数`x`は`number`型である」という宣言がされていることになります。実際、関数の中身を読むとそう書いてありますね。この関数は、引数`x`の型を`typeof`でチェックし、それが`number`でなければ例外を発生させます。ということは、この関数が正常終了するのは引数`x`が数値だった場合だけです。このことにより、`assertIsNumber`が正常終了したならば`x`が数値であると仮定してよく、それを型の世界で表すと`x`を`number`型として扱ってもよいということになります。このことを、`asserts x is number`という返り値の型で表しているのです。

使用例はこんな感じになります。

```ts
function assertIsNumber(x: unknown): asserts x is number {
  if (typeof x !== "number") {
    throw new Error("BOOM");
  }
}

// someValueはunknown型
const someValue: unknown = "foobar";

assertIsNumber(someValue);
// ここではsomeValueはnumber型

console.log(someValue.toFixed(1));
```

この例では最初`someValue`変数が`unknown`型だったのに、`assertIsNumber`関数呼び出しを通過した後は`number`型に変化しています。これが`asserts`型述語の効果です。

ちなみに、`asserts`型述語は、`asserts x is T`という構文の他に、単に`asserts x`とだけ書く構文もあります。これは、「関数が正常終了するならば`x`はtruthyな値（真偽値に変換すると`true`になる値）である」という意味になります。

`asserts`型述語の説明はこれだけです。とても簡単ですね（記事執筆時点でまだPRがマージされておらず、追加機能の議論もあるため実際にTypeScript 3.7がリリースされるときにはもう少し豪華になっているかもしれません）。

### `asserts`の使い道

この`asserts`型述語は、その名が示す通りアサーション関数をいい感じにサポートすることがそもそもの目的です。アサーション関数というのは条件を満たさなかったら例外を発生させる関数です。

アサーション関数は、テストコードや例外ベースのバリデーション処理などで使われがちですね。例えば、謎のデータを返す`getUnknownData()`関数をテストしたい場合を考えます。今回、実はこの関数が数値を、しかも3の倍数を返すことをテストしたいとしましょう。

数値かどうかテストできる`assertIsNumber`関数はあるけど、3の倍数かどうかテストできる関数は流石に無いので汎用の`asserts`関数を使うというシチュエーションを考えるとだいたいテストはこんな感じに書けます。

```ts
it('getUnknownData() は3の倍数を返す', ()=> {
  const data = getUnknownData();

  assertIsNumber(data);

  asserts(data % 3 === 0);
});
```

ただし、`assertIsNumber`は引数が数値でなければ例外を発生させる関数で、`assert`は引数が`true`でなければ例外を発生させる関数です。テストなので例外が発生すれば当然テストが落ちますね。

では、これがTypeScriptだとどうでしょうか。とりあえず`asserts`のことは一旦忘れて型を付けてみましょう。`getUnknownData()`は謎のデータを返すので、返り値は当然`unknown`です。


```ts
function getUnknownData(): unknown { return 123; }
function assertIsNumber(value: unknown): void { /* ... */ }
function assert(condition: boolean): void { /* ... */ }

it('getUnknownData() は3の倍数を返す', ()=> {
  const data = getUnknownData();

  assertIsNumber(data);

  assert(data % 3 === 0);
  //     ^^^^ ここでエラーが発生
});
```

上の定義だと、ソースコード中に示した通り`data % 3`のところでエラーが発生します。これは、`data`の型が`unknown`なので`%`という数値演算ができないからです。

実際のところ`assertIsNumber`をくぐり抜けたことで`data`が数値であることは保証されているのですが、従来はこれを我々は知っていてもTypeScriptコンパイラが知らなかったため、例えば`((data as number) % 3`のように`as`を使ってごまかす必要がありました（そもそも`data`を`any`とかにする禁断の技もありますが）。

今回の新機能であるところの`asserts`を使うと、この型エラーをいい感じに排除することができます。`assert`系の関数の型をいい感じに変えるといいですね。次のようにするとエラーが消えます（`assertIsNumber`関数だけ変えれば今回は大丈夫です。`assert`関数のほうはおまけです）。

```ts
function assertIsNumber(value: unknown): asserts value is number { /* ... */ }
function assert(condition: boolean): asserts condition { /* ... */ }
```

こうすると、`assertIsNumber`をエラーを出さずにくぐり抜けた時点で`data`の型が`number`となり、`data % 3`の型エラーを回避できるのです。

これが基本的な`asserts`型述語の使い方でした。応用編もあるのですが、それは本題でないので記事の後半で紹介します。

なお、一点誤解しないでいただきたいのは、**`asserts`と書いてもランタイムには一切影響を与えない**ということです。そもそもTypeScriptの大きな特徴は型がランタイムに影響を与えないということであり、`asserts`も例外ではありません。`asserts`が効果を発揮するのはあくまで型チェック時のみであり、実行時には無関係です。このことは次に説明する`asserts`の危険性にもすこし関わってきます。


## `asserts`の危険性

ここからがやっと本題です。いま紹介した便利な`asserts`型述語なのですが、実は**迂闊に使うと危険**なのです。**危険**というのはどういうことかというと、**せっかくTypeScriptが保証してくれている安全性を破壊してしまう**という意味です。極論、`asserts`の誤った使い方をしてしまうとTypeScriptを使っている意味が消え失せます。

`asserts`の危険性とは何かといういと、**嘘をついても怒られない**という点に集約されます。`asserts`による宣言は**完全に自己申告**であり、`asserts`で書かれた宣言を**TypeScriptは一切疑わずに信じ込んでしまう**のです。

ということは、最悪次のような宣言をしても問題なくコンパイルが通るということです。

```ts
function assertIsNumber(value: unknown): asserts value is number {
  // 間違って型が文字列かどうかチェックしてしまった！！！！！！！！！
  if (typeof value !== "string") {
    throw new Error("BOOM");
  }
}
```

この`assertIsNumber`は＾明らかに実装がおかしくて、返り値が`asserts value is number`というのは嘘っぱちです。しかし、TypeScriptはこれに文句を言いません。もちろん、先述の通り何かランタイムのチェックが自動で挿入されるわけでもありません。ということは、上の関数を使うとバグ発生へまっしぐらということです。実行時エラーが発生してしまうということは、TypeScriptの安全性が無に帰してしまったということです。

```ts:実行時エラーの例
const data = getUnknownData();
// number以外を弾いたつもり
assertIsNumber(data);
// ここではdataはnumber型（実装が変なので実際は文字列が来る可能性がある）

console.log(data.toFixed(10)); // 型チェックが通るけど実行時にエラーが発生
```

これが`asserts`型述語の危険性です。`asserts`と書いた時点でその宣言が正しいことを保証する義務はプログラマにあるのであり、TypeScriptに頼ることはできないのです。

よって、**`asserts`を使うときは自分が嘘をついていないか注意深く見直さなければいけない**ということです。コードレビュー中にコードに`asserts`が登場したら黄色信号、要注意ポイントです。

このように、TypeScriptの型チェックに頼らずに自ら注意しないといけないというのは、`any`型や`as`などの（自分で安全性を保証する必要があるという意味で）**TypeScriptの危険な機能**に共通する性質です。今回は、この危険な機能ファミリーにひとつ仲間が加わったということになります。ちなみに、`asserts`以外の危険な機能については筆者による以下の記事で詳しく取り扱っています。(そのうち向こうの記事にも追記します。）

- [敗北者のTypeScript](https://qiita.com/uhyo/items/aae57ba0734e36ee846a)

この記事で皆さんに一番伝えたかったことは、`asserts`はこのような**危険な機能**の一員であることを理解して使わなければならないということです。静的型付け言語は型をうっかり間違ってもコンパイラが指摘してくれることで安全性を担保できるのが良い点ですが、`asserts`を使う場合はそうもいかず、皆さんに安全性を担保する責任が一部生じてしまうのです。

`asserts`というのはある種のインターフェースであり、その宣言の真の意味は「この関数がこういう意味を持つことはプログラマが保証するから安心して使ってね」という意味なのです。

以上で一番伝えたかったことは終わりなのですが、この記事はもうすこし続きます。次の節では、`asserts`の本質に迫りつつこの点をもうちょっと深堀りしていきます。

## 型述語の本質

今回紹介した`asserts`という新機能は、ある意味では非常に革新的なのですが、ある意味では別に全然目新しくもない変更です。

というのも、TypeScriptには既に`is`を使う構文がありましたよね。それは、（`asserts`無しで）`x is T`という構文で表されるuser-defined type guardsです。多分、この`x is T`も（`asserts`ではない普通の）型述語と呼ばれています。今回`asserts`が加わったことで、型述語が`x is T`と`asserts x is T`の2種類になったわけです。

### 既存の型述語

この`x is T`はこんな感じで使うのでした。

```ts
function isNumber(value: unknown): value is number {
  return typeof value === "number";
}

const data = getUnknownData();

if (isNumber(data)) {
  // ここではdataはnumber型
  console.log(data.toFixed(0));
}
```

すでにお分かりの通り、同じ`is`というキーワードを使っているだけあって、この既存機能と今回の新機能はとても類似しています。特に、どちらも変数の型を絞り込めるという点で共通していますね。違うのはその方法です。

既存の型述語では、`x is T`という返り値の関数は真偽値を返さなければいけません。返り値が`true`ならば`x`が`T`型であるということになります（当然、これも自己申告です。`x is T`もまた危険な機能のひとつです）。

それに対し、`asserts`型述語では関数自体の返り値の型は`void`です。関数の返り値ではなく、関数が正常に終了したかどうか（例外を発生させなかったかどうか）によって`x is T`が満たされるかどうかが判定されるのです。従来の型述語は`if`文（あるいはほかの条件分岐構文）と組み合わせる必要があったのに対し、`asserts`は関数を呼ぶだけでいいというのが新しいですね。その代わり、例外という機構に頼らなければいけません。適材適所で使い分けましょう。

### 2つの型述語の本質的な共通点

さて、このように2種類の型述語の似ている点と異なる点を紹介しましたが、この2つにはもっと**本質的な共通点**があります。それはどちらも**制御フロー解析に影響を与えるロジックを関数として分離するための機能**であるという点です。

#### `x is T`と制御フロー

すなわち、そもそもTypeScriptには`if`文などを適切に解釈して型を絞りこむ機能があったわけです。これは`union`型などとも関わるTypeScriptの非常に重要な機能です。例えば、上の例などはわざわざ`isNumber`なんて関数を作らなくてもこのように書けます。

```ts:if文による型の絞り込みの例
const data = getUnknownData();

if (typeof data === "number") {
  // ここではdataはnumber型
}
```

これは、TypeScriptが`if`の条件部分に現れた`typeof data === "number"`という式をよしなに解釈して`data`を`number`型に絞り込んでいるということができます。正確性を犠牲にして言えば、これは`typeof data === "number"`という式の型が`data is number`型に推論されるという理解もできます。

ところが、この条件部分がより複雑になってくると2つの問題が発生します。1つは長い式を`if`文の中に直書きするのは辛いということ、もう1つはあまりに凝ったことをやるとTypeScriptの理解能力を超えてしまう（正しく型の絞り込みができない）ということです。

前者の問題については、単純に式を関数に押し込めて以下のような`isNumber`関数のようなものを作ってもよいのですが、それではTypeScriptは`isNumber`の意味を自動的には理解してくれず、`if`文の条件式に使っても何も起きません。

```ts
// data is number と明示的に書かないと if (isNumber(value)) のように書いても絞り込まれない！
function isNumber(data: unknown) {
  return typeof data === "number";
}
```

自動的にこれをやってくれないのは、恐らく2つ目の問題のためでしょう。つまり、関数に切り出したいほど複雑なロジックの場合は処理を追うのはすぐに限界が来て、実用可能なレベルで自動推論するのは無理があります。そのため、返り値に`x is T`を書くことで明示的に宣言させる仕組みになっているのです。

そして、前述の通り`x is T`はTypeScriptが何の保証も行ってくれないため危険なのですが、それにも関わらず偉い点は、**危険性が自動的に関数の中に閉じ込められる**という点です。

レビュー時に関数の返り値の型に`data is number`などと書いてあったらそれは黄色信号なのですが、その場合どこに注意を払えばいいのかは極めて明白です。そう、まさにその関数の中ですね。

TypeScriptの危険な機能を扱う際に最もやってはいけないのは危険性の影響範囲を制御できずに無秩序に拡散させてしまうこと（`any`型の関数を作って放置するとか）なのですが、この`x is T`型述語は自動的に危険性が関数の中に閉じ込められる（すなわち、その関数が安全なことを人の手で確かめれば外まで見なくても大丈夫）ためこの問題が発生しないのです。それゆえに、TypeScriptにおける危険性マネジメントにおいて、この`x is T`というのはとても取り回しがいい存在です。

#### `asserts x is T`と制御フロー

さて、ちょっと回り道しましたが話を`asserts`型述語に戻しましょう。今まで説明したことは全て`asserts`型述語に当てはまります。例えば`assertsNumber`関数というのは、次のようなロジックを関数に閉じ込めたものであるということができます。

```ts
// dataはunknown型
const data = getUnknownData();

if (typeof data !== "number") {
  throw new Error("BOOM");
}
// ここではdataはnumber型
console.log(data.toFixed(0));
```

実は、TypeScriptはこのように`throw`含んだ制御フローをやはりいい感じに解析してくれて、上の例くらいなら最終的に`data`が`number`型であることを見抜いてくれます。

しかし、`throw`を含むロジック（上の例だと`if`文の3行ですね）を別の関数に切り出すと、TypeScriptは自動的に解析を行ってくれなくなります。それはちょうど普通の`x is T`と同じ理由によるものです。

ですから、まさに同じ議論を適用することによって`asserts x is T`という型述語の存在意義が説明できます。まとめ直すと、例外が発生する条件などが複雑化したときにTypeScriptが制御フローだけから自動でその意味を推論するのは無理があるため、ユーザーが明示的に`asserts x is T`型述語を書くことによって、この関数が`throw`しない場合に何が保証されるのかを宣言できるようにしているのです。

もちろん、メリットデメリットも`x is T`と全く同様です。デメリットはTypeScriptによる検査がなく自分の責任で安全性を保証しなければいけないことであり、メリットはそのような危険性がやはり関数内に閉じ込められていることです。

ここまでの説明により、もはや2種類の型述語の共通点は明らかですね。それは、どちらも制御フローによるロジックを関数に切り出すための機能であるということです。唯一の違いは、`x is T`は条件分岐を切り出す一方で`asserts x is T`は例外を切り出すという点です。

2つの違いはたったこれだけであり、ゆえにTypeScript 3.7の`asserts`型述語というのはある意味では全然目新しくないのです。既に`x is T`について我々が知っていたことがそのまま`asserts x is T`にも当てはまるのであり、既に`x is T`の使い方をわきまえていたならば`asserts x is T`を受け入れるのは非常に簡単でしょう。そうでない方もご安心ください。この記事をここまで読んだ貴方は大丈夫です。

## 型述語の使いどころと使いかた

結論から言ってしまえば、（既存の`x is T`型述語と同じく）`asserts x is T`というのはライブラリの型を付けたいとき（あるいはプロジェクト内でもutil的な関数に型を付けたいとき）に最も重宝するでしょう。

これらの型述語を使った時点で関数の中は危険領域なのですから、あまりに濫用するとTypeScriptに任せられない部分が増えて開発者の負担が増えてしまいます。よって、これらの機能が作り出す危険領域は最小限にすべきです。

その観点からして最も安全なのは、既成のライブラリを使う場合です。既成のライブラリに`asserts`型述語などがついていれば、（ライブラリの型定義が嘘をついていないという前提のもとではありますが）我々が何も負担することなく型述語の利便性を享受できます。

ですから、ライブラリを作る場合は積極的にこれらの型を使っていきたいですね。

しかしそれだけではなく、実際のところ、プログラムで扱うデータに厳密に型を付けようとすればするほど型述語のお世話になる機会は増えていきます。（筆者はあまり好きではないのですが）Branded typeのようなテクニックを使う際にも型述語は有効でしょう。とにかく一番重要なのは、（[敗北者のTypeScript](https://qiita.com/uhyo/items/aae57ba0734e36ee846a)で説明したことの繰り返しですが）危険領域を小さく抑えることです。危険領域を小さく抑えることにより安全性を担保するための労力が小さくなり、バグの危険性も小さくなります。これこそが型述語のうまい使い方と言えます。

この記事を読んだ皆さんはやらないでしょうが、`asserts x is T`などと書いておきながらそれが嘘であるというのは最悪のケースです。関数内の安全性を保証しなかったばかりに、関数外部まで危険性（型システムで防げないバグの可能性）の影響が及んでいることになります。これはTypeScriptを使っている意味を大きく損ないますから、避けなければいけません。本当に気をつけましょう。

## まとめ

この記事ではTypeScript 3.7に入る予定の新機能、`asserts x is T`型述語を紹介し、その危険性や望ましい扱い方を解説しました。

といっても、途中で説明した通りこの新機能は既存の`x is T`と非常に似通ったものであり、それゆえこの記事の内容はほとんどが両者に当てはまります。

結論をもう一度繰り返すと、これらの型述語をTypeScirptは盲信します。よって型述語を使用した時点で関数の中は危険地帯である、ゆえに危険地帯の範囲を最低限にせよ、ということです。

危険性ばかりを強調しましたが、危険だから使うななどということは言いません。むしろ、筆者はこの危険性をいかに乗りこなすかがTypeScriptの醍醐味であると考えています。TypeScriptの非常に強力な型システムの恩恵を受けるには危険性は避けて通ることはできません。よく分からず危険性を使うのが初心者、危険性を避けるのが中級者、危険性を乗りこなすのが上級者です。

皆さんもぜひ`asserts`を使いこなして快適なTypeScriptライフを送ってください。

といってもTypeScript 3.7がリリースされるのは11月末とかそれくらいになりそうですが。ちなみに、あのoptional chaining`x?.y`が入るのもTS 3.7の予定です。未来ですね。

## おまけ

結論として`asserts`型述語は既存の型述語と本質的に同じであると述べましたが、その一方で少し前にどこかで「`asserts`はある意味で非常に革新的である」とも述べました。せっかくなので、この記事ではその部分にも触れようと思います。

`asserts`型述語の何が革新的なのかというと、（今の所返り値が`void`であるという制限があるものの）**関数を呼ぶだけで変数の型を変えられる**という点です。従来TypeScriptにおいて一度宣言された関数の型を変えるのは困難であり、それこそ条件分岐などを使って絞り込むしかない状態でした。それが、関数を呼ぶだけでよいとなると夢が広がります。

さっそく例をお見せしましょう。なお、例は全て記事の冒頭で紹介したプルリクのコミット`2c36249ed6bd63f4fd66813985ae2dff681349a5`で動作確認しています。

### オブジェクトをデフォルト値で埋める関数

関数がオプションをオブジェクトでまとめて受け取るというのは非常によくあるシチュエーションです。そして、オプションたちは往々にして省略可能です。もし受け取ったオプションが省略されていた場合はデフォルト値を使用することになるでしょう。

`asserts`型述語を用いることで、この処理をいい感じに書けます。やや長いですが例を見ましょう。

```ts
interface Options {
  foo: string;
  bar: number;
}

function useOptions(options: Partial<Options>) {
  // これはエラー (options.fooはundefinedかもしれないので）
  console.log(options.foo.length);

  // オプションを埋める
  fillOptions(options);
  // これはエラーにならない！！！！！
  console.log(options.foo.length);
}

function fillOptions(options: Partial<Options>): asserts options is Options {
  if (options.foo === undefined) {
    options.foo = "";
  }
  if (options.bar === undefined) {
    options.bar = 0;
  }
}
```

この例で登場する`Options`型は`foo`と`bar`の2つのプロパティを持つオブジェクトの型です。次に定義されている関数`useOptions`はこのオプションたちをオブジェクトで受け取りますが、`foo`や`bar`は省略可能ということを表現するために引数の型は`Partial<Options>`型になっています。

となると、これをいきなり`options.foo.length`のように使うのはまずいです。`options.foo`は`string`型のはずでしたが、今回は`Partial`によって`options.foo`が存在しない、すなわち`undefined`である可能性が発生しているからです。

今回鍵となるのが`fillOptions`関数です。この関数を呼ぶとあら不思議、なんとその後`options.foo`を使ってもエラーにならないのです。

その秘密はもちろん`asserts`型述語です。今回`fillOptions`の返り値は`asserts options is Options`です。これはつまり、`fillOptions`を呼び出して以降は`options`は（`Partial<Options>`ではなく）`Options`であるという宣言になります。これにより、`fillOptions`を呼び出して以降は`options.foo`が`undefined`である可能性が消えるので`options.foo.length`としてもエラーにならなかったのです。

もちろん、この記事で説明した通り、`fillOptions`のこの宣言が正しいことを保証するのは書いた人の役目です。今回は確かに`fillOptions`がこの宣言通りの動作をしている（`foo`や`bar`が`undefined`である可能性を潰している）ことが分かりますから、これで大丈夫です。

このように、**`asserts`型述語によってオブジェクト等に対するミューテーション（書き換え）を型で表現することができます**。これは今までにない非常に革新的な機能です。

### Builderパターン再び

さらに、実は`asserts`型述語によって変えられるのは引数の型だけでなく、実は`this`の型も変えることができます。これを利用して**Builderパターン**をTypeScriptを書いてみましょう。「再び」というのは筆者の以下の既存記事のことを指しています。

- [TypeScriptで超型安全なBuilderパターン](https://qiita.com/uhyo/items/69341387ed6a493b5df5)

上の記事ではBuilderオブジェクトはメソッドチェーンの形で使うことを強いられていましたが、`asserts`型述語を手に入れた我々にはもはやそのような制限はありません。細かなアイデアは上の記事と同じなので、長々とした説明は飛ばして早速結果をお見せします。また、本質的ではないのでランタイム実装は省略して型部分だけ取り出しました。

```ts
type Builder<Props, Result> = ({} extends Props
    ? {
        build: () => Result;
      }
    : {}) & BuilderMethods<Props, Result>

interface BuilderMethods<Props, Result> {
    set<Key extends keyof Props>(key: Key, value: Props[Key]): asserts this is Builder<Pick<Props, Exclude<keyof Props, Key>>, Result>
}

// ----- 使用例 -----
// builder は foo と bar を指定する必要があるBuilder
declare const builder: Builder<{
    foo: string;
    bar: number;
}, string>
// まずfooを指定
builder.set("foo", "foobar");
// 次にbarを指定
builder.set("bar", 123);

// するとbuild()が呼べる
const result: string = builder.build();
```

前半がBuilderパターンの実装で後半が例です。例は、まずなんらかの方法で`Builder<{ foo: string; bar: number}, string>`という型のビルダー`builder`を入手します。型が示す通り、これは`foo`と`bar`を指定すると`build`が呼べるようになります。今回は実装上の都合で、`foo`や`bar`という値は`set`メソッドを通じて指定することにしました。一つずつ`set`メソッドを呼び出すことで`foo`と`bar`の値を指定します。

すると、例の最後の行のようにビルダーの`build()`メソッドが呼べるようになりました。

このように`set`メソッドで必要な値を供給して`build`メソッドで値を生成するというのがビルダーの使い方です。安全にビルダーを使うためには、`build`は必要な値を全部`set`で供給するまで呼び出してはいけません。今回はこの制限が型レベルで実現しており、次のようなものは型エラーで弾くことができます。

```ts:エラー例
declare const builder2: Builder<{
    foo: string;
    bar: number;
}, string>

// まずfooを指定
builder2.set("foo", "foobar");

// まだbarを指定していないのでこれは型エラー
// TS2339: Property 'build' does not exist on type
// 'BuilderMethods<Pick<{ foo: string; bar: number; }, "bar">, string>'.
builder2.build()
```

今回は別のビルダー`builder2`を使いますが、`set`で`bar`を指定し忘れています。よって、`builder2.build()`を呼び出そうとしても型エラーになります。

また、次の例もエラーです。

```ts
declare const builder3: Builder<{
    foo: string;
    bar: number;
}, string>

// まずfooを指定
builder3.set("foo", "foobar");

// もう一度fooを指定しようとすると型エラー
// error TS2345: Argument of type '"foo"' is not assignable to parameter of type '"bar"'.
builder3.set("foo", "吉野家")
```

このように、`asserts`型述語を活用（悪用？）することで、関数やメソッドの呼出によって型を操作することができ、「メソッドの呼出の順番」などといったものに対する型チェックを（自由自在にとまでは行きませんが）行うことができます。これはやばいですね。

以上です。