TypeScript Handbook を読む (2. Variable Declarations)

[前回](http://qiita.com/murank/items/9c1f633545842c953947)に続き、 [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/basic-types.html) を読んでいく。

1. [Basic Types](http://qiita.com/murank/items/9c1f633545842c953947)
1. Variable Declarations (今ココ)
1. [Interfaces](http://qiita.com/murank/items/553276c3dc2eec9bfe47)
1. [Classes](http://qiita.com/murank/items/7e8f15225bc8259e9bf3)
1. [Functions](http://qiita.com/murank/items/e310ce68ed5a0058d42d)
1. [Generics](http://qiita.com/murank/items/144b686d34fd433a97a3)
1. [Enums](http://qiita.com/murank/items/cc487562b21bae3728ba)
1. [Type Inference](http://qiita.com/murank/items/e67ad7426020321228e9)
1. [Type Compatibility](http://qiita.com/murank/items/606efb0615294a6826e3)
1. [Advanced Types](http://qiita.com/murank/items/bc1776efbecbd2120d94)
1. [Symbols](http://qiita.com/murank/items/04b3b31caa677750f240)
1. [Iterators and Generators](http://qiita.com/murank/items/7ceec53dde886f31a682)
1. [Modules](http://qiita.com/murank/items/0fc4e059be8ea5e1c274)
1. [Namespaces](http://qiita.com/murank/items/0dfad4a0bb91aaa6c97b)
1. [Namespaces and Modules](http://qiita.com/murank/items/0be1978a3515fc76e7ba)
1. [Module Resolution](http://qiita.com/murank/items/5f11466474d49498bbd5)
1. [Declaration Merging](http://qiita.com/murank/items/ec90ba04edab1ea3718e)
1. [JSX](http://qiita.com/murank/items/90164aa9a3bed3c49a80)
1. [Decorators](https://qiita.com/murank/items/580e34883e0832ca7aed)
1. [Mixins](https://qiita.com/murank/items/e77ad8359ed1fd82822d)
1. [Triple-Slash Directives](https://qiita.com/murank/items/faff57883338ebdeda12)
1. [Type Checking JavaScript Files](https://qiita.com/murank/items/588eaddfe8af59914658)


# Variable Declarations
[原文](https://www.typescriptlang.org/docs/handbook/variable-declarations.html)


## Variable Declarations
`let` と `const` は JavaScript でも比較的新しい変数宣言です。
`let` は `var` とよく似ていますが、JavaScript を使う中でよくある "gothas" を避けることができます。
`const` は変数への再代入を禁止する、`let` の拡張版です。

## `var` declarations
従来の JavaScript での変数宣言には `var` キーワードが使われてきました。

```js:JavaScript
var a = 10;
```
見て分かる通り、`a` という変数を宣言し、値を `10` に設定しています。

メソッドの中で変数宣言することもできますし、

```js:JavaScript
function f() {
    var message = "Hello, world!";

    return message;
}
```

他のメソッドから変数にアクセスすることもできます。

```js:JavaScript
function f() {
    var a = 10;
    return function g() {
        var b = a + 1;
        return b;
    }
}

var g = f();
g(); // 11
```

上記の例では `f` のなかで宣言された変数 `a` を `g` がキャプチャしています。
どこで `g` が呼び出されても、`a` の値は `f` の中で宣言されている `a` の値に拘束されます。

一度 `f()` の実行が終了すれば、`g()` が呼ばれていても `a` を読み取り/変更することができます。

```js:JavaScript
function f() {
    var a = 1;

    a = 2;
    var b = g();
    a = 3;

    return b;

    function g() {
        return a;
    }
}

f(); // 2
```
>いまいち何の例か分かってないけど、一度 `b` に代入してるから、その後に `a` を変更しても影響ないということが言いたいのかな？

### Scoping rules
`var` のスコープは他の言語と比べて奇妙なところがあります。

```js:JavaScript
function f(shouldInitialize: boolean) {
    if (shouldInitialize) {
        var x = 10;
    }

    return x;
}

f(true);  // 10
f(false); // undefined
```
変数 `x` は *`if` ブロックの中で* 宣言されているにも関わらず、ブロックの外からでもアクセスすることができます。
なぜなら、`var` で宣言された変数は、変数を宣言した関数、モジュール、名前空間、グローバルスコープの中であれば、それがブロックを含むかどうかによらず、どこからでもアクセスすることができるためです。
これは *`var` スコープ* や *関数スコープ* とも呼ばれます。
関数の引数も関数スコープとなります。
>悪名高き「変数の巻き上げ」というやつだね

このスコープの規則はある種の間違いを引き起こしがちです。
間違いの元になる問題のひとつは、同じ変数を複数回宣言してもエラーにならないことです。

```js:JavaScript
function sumMatrix(matrix: number[][]) {
    var sum = 0;
    for (var i = 0; i < matrix.length; i++) {
        var currentRow = matrix[i];
        for (var i = 0; i < currentRow.length; i++) {
            sum += currentRow[i];
        }
    }

    return sum;
}
```
`i` は同じ *関数スコープの* 変数を参照することになるため、内側の `for` ループで誤って `i` を更新してしまっています。


### Variable capturing quirks
以下のコードを実行すると何が起きるでしょう？

```js:JavaScript
for (var i = 0; i < 10; i++) {
    setTimeout(function() { console.log(i); }, 100 * i);
}
```

その結果は以下のようになります。

```
10
10
10
10
10
10
10
10
10
10
```

JavaScript 開発者であればよく知っている挙動かもしれませんが、大抵の人は以下のような出力を想像します。

```
0
1
2
3
4
5
6
7
8
9
```

前に変数のキャプチャについて説明しましたが、`setTimeout` に渡した関数はすべて同じスコープ内の同じ `i` を参照しています。

つまり、`setTimeout` は数ミリ秒に実行されますが、それは `for` ループを抜けた *後* になります。
その時点では `i` は `10` になっているため、各関数が `10` を出力するというわけです。

これの回避策としては、各繰り返しで `i` をキャプチャするために即時関数 (IIFE) を使うことです。

```js:JavaScript
for (var i = 0; i < 10; i++) {
    // 今の 'i' の値をメソッドに引き渡すことで
    // 今の状態をキャプチャさせる
    (function(i) {
        setTimeout(function() { console.log(i); }, 100 * i);
    })(i);
}
```

一見、奇妙に見えますが、この実装パターンは非常によく知られています。
引数の `i` は実際には `for` ループで宣言されている `i` を隠蔽してしまいますが、元の変数名と合わせているため、元のループブロックをそれほど修正せずに済みます。


## `let` declarations
`let` 文は宣言用のキーワードを除き、`var` と同じように使用できます。

```ts:TypeScript
let hello = "Hello!";
```
```js:JavaScript
var hello = "Hello!";
```

### Block-scoping
`let` を使用して宣言された変数は *レキシカルスコープ (ブロックスコープ)* になります。
`var` で宣言された変数は関数内までスコープが広がるのに対し、ブロックスコープの変数はそれを含むブロック、または `for` ループ内でしかアクセスできません。

```ts:TypeScript
function f(input: boolean) {
    let a = 100;

    if (input) {
        // ここからでも 'a' にアクセスすることができる
        let b = a + 1;
        return b;
    }

    // エラー。 ここでは 'b' にはアクセスできない
    return b;
}
```
```ts:JavaScript
function f(input) {
    var a = 100;
    if (input) {
        // ここからでも 'a' にアクセスすることができる
        var b = a + 1;
        return b;
    }
    // エラー。 ここでは 'b' にはアクセスできない
    return b;
}
```
ここでは `a` と `b` という 2 つの変数を宣言しています。
`a` のスコープは `f` 全体になるのに対し、`b` のスコープは `if` ブロック内に制限されます。
>コンパイル後には `var` に置き換わるため、あくまでもコンパイル時のチェックである点に注意
>(まあそれで充分なんだけど…)

`catch` 句でも同じルールが適用されます。

```ts:TypeScript
try {
    throw "oh no!";
}
catch (e) {
    console.log("Oh well.");
}

// エラー。ここでは 'e' にアクセスできない
console.log(e);
```

ブロックスコープの変数の他の特徴としては、変数宣言の前に読み書きできないことが挙げられます。
変数自体はスコープ内に "存在" するものの、変数宣言までは *temporal dead zone* に属するものとして扱われます。

```ts:TypeScript
a++; // エラー。まだ 'a' は宣言していない。
let a;
```

また、変数宣言の前でも変数を *キャプチャ* することはできますが、変数宣言前にメソッドを呼び出すことは禁止されています。
ES2015 をターゲットとしているのであれば実行時にエラーになりますが、今の TypeScript ではコンパイルエラーにはなりません。

```ts:TypeScript
function foo() {
    // 'a' をキャプチャすることはできる
    return a;
}

// 不正な 'foo' の呼び出し ('a' の宣言前)
// 実行時にエラーになる
foo();

let a;
```

temporal dead zones についての詳細は [Mozilla Developer Network](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/let) を参照してください。

### Re-declarations and Shadowing
`var` では何回 変数宣言してもエラーにはなりませんでした。

```js:JavaScript
function f(x) {
    var x;
    var x;

    if (true) {
        var x;
    }
}
```
上記の例では、すべての `x` は *同じ* `x` を参照しています。
これはバグの原因となりかねませんが、`let` では変数宣言は 1 回だけしか許可されません。

```ts:TypeScript
let x = 10;
let x = 20; // エラー。同じスコープでは 'x' を再宣言できない
```

ブロックスコープの変数同士である必要はありません。

```ts:TypeScript
function f(x) {
    let x = 100; // エラー: 引数と重複している
}

function g() {
    let x = 100;
    var x = 100; // エラー: 'x' の宣言を複数持てない
}
```

ただし、ブロックスコープの変数を関数スコープの変数と一緒に宣言できないというわけではありません。
ブロックスコープの変数を異なるブロックで宣言するだけです。

```ts:TypeScript
function f(condition, x) {
    if (condition) {
        let x = 100;
        return x;
    }

    return x;
}

f(false, 0); // 0
f(true, 0);  // 100
```

ネストしたスコープ内で新しい変数を宣言することを *シャドーイング* と呼びます。
これはある種のバグを防ぐものの、別のバグの原因にもなるため、諸刃の剣でもあります。
例えば、前述の `sumMatrix` 関数を `let` を使って記述してみましょう。

```ts:TypeScript
function sumMatrix(matrix: number[][]) {
    let sum = 0;
    for (let i = 0; i < matrix.length; i++) {
        var currentRow = matrix[i];
        for (let i = 0; i < currentRow.length; i++) {
            sum += currentRow[i];
        }
    }

    return sum;
}
```
```js:JavaScript
function sumMatrix(matrix) {
    var sum = 0;
    for (var i = 0; i < matrix.length; i++) {
        var currentRow = matrix[i];
        for (var i_1 = 0; i_1 < currentRow.length; i_1++) {
            sum += currentRow[i_1];
        }
    }
    return sum;
}
```
>変数名が重複したら自動的に連番を振って、名前が重複しないようにしてくれるのね

内側のループの `i` が外側のループの `i` を隠蔽するため、これは正しく動作します。

意図が明確なコードを書くためにも、*通常は* シャドーイングは避けるべきですが、いくつかのケースでは有用なこともあるため、自身の判断で適切に使用するようにしてください。


### Block-scoped variable capturing
`var` 宣言のところでキャプチャされた変数の振る舞いについて説明しました。
より直感的に説明すると、スコープが実行されるたびに変数の "環境" が作成され、その環境 (とキャプチャされた変数) はスコープの実行が終わった後も存在し続けるということです。

```ts:TypeScript
function theCityThatAlwaysSleeps() {
    let getCity;

    if (true) {
        let city = "Seattle";
        getCity = function() {
            return city;
        }
    }

    return getCity();
}
```
環境内に `city` がキャプチャされているため、`if` ブロックを抜けた後でも変数にアクセスすることができます。

前の方に出てきた `setTimeout` の例は、`for` ループの繰り返しごとに変数の場外をキャプチャするために IIFE を使用する必要がありました。
これはつまり、変数をキャプチャするために新しい変数環境を作成していたということです。

ループ処理において、`let` 宣言はまったく違う振る舞いを見せます。
ループ全体に対して新しい環境を作成するのではなく、*繰り返しごと* に新しいスコープを作成するのです。
これはまさに IIFE を使用して実現しようとしていたことであり、`let` 宣言を使用して `setTimeout` の例を書き直してみましょう。

```ts:TypeScript
for (let i = 0; i < 10 ; i++) {
    setTimeout(function() { console.log(i); }, 100 * i);
}
```
```js:JavaScript
var _loop_1 = function (i) {
    setTimeout(function () { console.log(i); }, 100 * i);
};
for (var i = 0; i < 10; i++) {
    _loop_1(i);
}
```
>`for` 文の外で `i` を宣言してしまうと効果がないので要注意

これの実行結果は以下のようになります。

```ts:TypeScript
0
1
2
3
4
5
6
7
8
9
```

## `const` declarations
他の変数宣言方法として、`const` 宣言があります。

```ts:TypeScript
const numLivesForCat = 9;
```
```js:JavaScript
var numLivesForCat = 9;
```
`const` は `let` と似ていますが、名前の表す通り、一度束縛した値を変更することはできません。
つまり、`let` と同じスコープルールに従いますが、変数に再代入することはできないということです。

ただし、参照先の変数が *不変* であるということと混同しないようにしてください。

```ts:TypeScript
const numLivesForCat = 9;
const kitty = {
    name: "Aurora",
    numLives: numLivesForCat,
}

// エラー
kitty = {
    name: "Danielle",
    numLives: numLivesForCat
};

// すべて OK
kitty.name = "Rory";
kitty.name = "Kitty";
kitty.name = "Cat";
kitty.numLives--;
```
>他の言語と同じように、オブジェクト (や配列) を定数宣言しても、その中身までは定数にならないということ。

なにかしらの対応を取らない限り、`const` 変数の内部状態は依然として変更可能なままです。
TypeScript ではオブジェクトのメンバを `readonly` に設定することができます。
詳細は [Interfaces の章](http://qiita.com/murank/items/553276c3dc2eec9bfe47) を参照してください。


## `let` vs. `const`
似たようなスコープの振る舞いを持つ変数宣言が 2 つあるため、どちらを使用するべきか聞かれることがよくあります。
そのような広範な質問に対する答えは「場合による」です。

[最小権限の原則](https://ja.wikipedia.org/wiki/%E6%9C%80%E5%B0%8F%E6%A8%A9%E9%99%90%E3%81%AE%E5%8E%9F%E5%89%87) に従えば、変更することのない変数にはすべて `const` を使用するべきです。
その根拠としては、これまで変更する必要のなかった変数について、何も考えずに値を書き換えるのではなく、変数への再代入が本当に必要かきちんと検討するべきだからです。
また、`const` を使用することでデータの流れがより追いかけやすくなります。

最終的には、自分自身が最適と判断したものを採用するか、可能であればチームの他の人と相談してください。


## Destructuring
TypeScript は ECMAScript 2015 の機能である変数の分割 (destructuring) に対応しています。
完全なリファレンスは [Mozilla Developer Network](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) を参照してください。

### Array destructuring
分割代入の一番簡単な例は変数の分割代入です。

```ts:TypeScript
let input = [1, 2];
let [first, second] = input;
console.log(first); // 1
console.log(second); // 2
```
```js:JavaScript
var input = [1, 2];
var first = input[0], second = input[1];
console.log(first); // 1
console.log(second); // 2
```

この例では `first` と `second` という 2 つの新しい変数を作成しています。
インデックスを使用しても同じことができますが、こちらの方がより便利です。

```ts:TypeScript
first = input[0];
second = input[1];
```

分割代入は宣言済みの変数に対しても適用可能です。

```ts:TypeScript
// 変数の入れ替え
[first, second] = [second, first];
```
```js:JavaScript
// 変数の入れ替え
_a = [second, first], first = _a[0], second = _a[1];
var _a;
```
>一時変数の宣言が後に来るのが気持ち悪い…

メソッドの引数に適用することもできます。

```ts:TypeScript
function f([first, second]: [number, number]) {
    console.log(first);
    console.log(second);
}
f([1, 2]);
```
```js:JavaScript
function f(_a) {
    var first = _a[0], second = _a[1];
    console.log(first);
    console.log(second);
}
f([1, 2]);
```

`...` を使って変数の残り部分を受け取ることができます。

```ts:TypeScript
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // 1
console.log(rest); // [ 2, 3, 4 ]
```
```js:JavaScript
var _a = [1, 2, 3, 4], first = _a[0], rest = _a.slice(1);
console.log(first); // 1
console.log(rest); // [ 2, 3, 4 ]
```

もちろん、JavaScript と同じように後続の要素を無視することができます。

```ts:TyepScript
let [first] = [1, 2, 3, 4];
console.log(first); // 1
```
```js:JavaScript
var first = [1, 2, 3, 4][0];
console.log(first); // 1
```

他の要素を無視することもできます。

```ts:TypeScript
let [, second, , fourth] = [1, 2, 3, 4];
```
```js:JavaScript
var _a = [1, 2, 3, 4], second = _a[1], fourth = _a[3];
```

### Object destructuring
オブジェクトを分割代入することもできます。

```ts:TypeScript
let o = {
    a: "foo",
    b: 12,
    c: "bar"
}
let { a, b } = o;
```
```js:JavaScript
var o = {
    a: "foo",
    b: 12,
    c: "bar"
};
var a = o.a, b = o.b;
```
この例では `o.a`、`o.b` から新しい変数 `a`、`b` を作成しています。
ここでは不要な `c` を無視しています。

配列の時と同様に、変数宣言なしに代入することも可能です。

```ts:TypeScript
({ a, b } = { a: "baz", b: 101 });
```
```js:JavaScript
(_a = { a: "baz", b: 101 }, a = _a.a, b = _a.b);
var _a;
```
この時、文を括弧で囲む必要がある点に注意してください。
JavaScript では通常、`{` はブロックの始まりとしてパースされるためです。

`...` を使って残りの要素を集めた変数を作ることもできます。

```ts:TypeScript
let { a, ...passthrough } = o;
let total = passthrough.b + passthrough.c.length;
```
```js:JavaScript
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
            t[p[i]] = s[p[i]];
    return t;
};
var a = o.a, passthrough = __rest(o, ["a"]);
var total = passthrough.b + passthrough.c.length;
```
>コンパイル後のコードががが…


#### Property renaming
別名で分割代入することもできます。

```ts:TypeScript
let { a: newName1, b: newName2 } = o;
```
```js:JavaScript
var newName1 = o.a, newName2 = o.b;
```

この構文を見て混乱しているかもしれませんが、`a: newName1` は “`a` を `newName1` とする” という意味です。
左から右に処理するため、自分で書くとしたら以下のようになります。

```ts:TypeScript
let newName1 = o.a;
let newName2 = o.b;
```

紛らわしいことに、ここでのコロンは型を表して *いません*。
もし型も指定する場合、分割代入全体の後ろに書く必要があります。

```ts:TypeScript
let { a, b }: { a: string, b: number } = o;
```
```js:JavaScript
var a = o.a, b = o.b;
```
>型指定と別名の指定を同時にする場合、以下のようにする。
>
>```ts:TypeScript
>let { a: newName1, b: newName2 }: { a: string, b: number } = o;
>```


#### Default values
プロパティが存在しない場合のデフォルト値は以下のように指定します。

```ts:TypeScript
function keepWholeObject(wholeObject: { a: string, b?: number }) {
    let { a, b = 1001 } = wholeObject;
}
```
```js:JavaScript
function keepWholeObject(wholeObject) {
    var a = wholeObject.a, _a = wholeObject.b, b = _a === void 0 ? 1001 : _a;
}
```

この例では `keepWholeObject` は `wholeObject` 以外にプロパティ `a` と (引数に プロパティ `b` が存在しなくても) プロパティ `b` を保持することになります。


### Function declarations
分割代入はメソッドにも適用できます。

```ts:TypeScript
type C = { a: string, b?: number }
function f({ a, b }: C): void {
    // ...
}
```
```js:JavaScript
function f(_a) {
    var a = _a.a, b = _a.b;
    // ...
}
```

引数のデフォルト値を指定することはよくありますが、分割代入と一緒にデフォルト値を指定する場合はトリッキーになります。
まず最初に以下のパターンを覚えてください。

```ts:TypeScript
function f({ a="", b=0 } = {}): void {
    // ...
}
f();
```
```js:JavaScript
function f(_a) {
    var _b = _a === void 0 ? {} : _a, _c = _b.a, a = _c === void 0 ? "" : _c, _d = _b.b, b = _d === void 0 ? 0 : _d;
    // ...
}
f();
```

続いて、分割代入されたプロパティのうち、任意のプロパティにデフォルト値を与える方法を覚えてください。

```ts:TypeScript
function f({ a, b = 0 } = { a: "" }): void {
    // ...
}
f({ a: "yes" }); // OK、デフォルト値 b = 0 が使用される
f(); // OK、デフォルト値 { a: "" } が使用され、さらにデフォルト値 b = 0 が使用される
f({}); // エラー、引数を指定する場合には 'a' は必須
```
```js:JavaScript
function f(_a) {
    var _b = _a === void 0 ? { a: "" } : _a, a = _b.a, _c = _b.b, b = _c === void 0 ? 0 : _c;
    // ...
}
f({ a: "yes" }); // OK、デフォルト値 b = 0 が使用される
f(); // OK、デフォルト値 { a: "" } が使用され、さらにデフォルト値 b = 0 が使用される
f({}); // エラー、引数を指定する場合には 'a' は必須
```

分割代入は注意して使用するようにしてください。
これまでの例で見てきた通り、一番単純な分割代入であってもコードが紛らわしいものになります。
深くネストした分割代入は例えリネーム、デフォルト値、型宣言といったものを組み合わせていなくても、*非常に* 分かりづらいものになります。
分割代入の式はできるだけ小さく、単純になるようにしてください。
また、いつでも自分自身で分割代入するコードを書くことができます。


### Spread
スプレッド演算子 (`...`) は分割代入とは逆のもので、配列を他の配列内に、オブジェクトを他のオブジェクト内に展開することができます。

```ts:TypeScirpt
let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second, 5];
```
```js:JavaScript
var first = [1, 2];
var second = [3, 4];
var bothPlus = [0].concat(first, second, [5]);
```
この例では、bothPlus は `[0, 1, 2, 3, 4, 5]` となります。
スプレッドは `first` と `second` のシャローコピーを作成しますが、スプレッドによって変更されることはありません。

オブジェクトを展開することもできます。

```ts:TypeScript
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, food: "rich" };
```
```js:JavaScript
var __assign = (this && this.__assign) || Object.assign || function(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
            t[p] = s[p];
    }
    return t;
};
var defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
var search = __assign({}, defaults, { food: "rich" });
```
この例では、`search` は `{ food: "rich", price: "$$", ambiance: "noisy" }` となります。
オブジェクトの展開は配列の展開よりも複雑です。
配列の展開と同じように左から右に処理していきますが、結果はオブジェクトのままになります。
つまり、前に展開したオブジェクトのプロパティが、より後に展開したオブジェクトで上書きされるということです。
先ほどの例を少し変えてみましょう。

```ts:TypeScript
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { food: "rich", ...defaults };
```
```js:JavaScript
var __assign = (this && this.__assign) || Object.assign || function(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
            t[p] = s[p];
    }
    return t;
};
var defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
var search = __assign({ food: "rich" }, defaults);
```
こうすると `defaults` の `food` プロパティが `food: "rich"` を上書きしますが、これは期待する動作ではありません。

オブジェクトの展開にはいくつかの制約があります。
1 つ目の制限は [オブジェクト自身の列挙可能なプロパティ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Enumerability_and_ownership_of_properties) のみが展開対象となる点です。
つまり、基本的にオブジェクトインスタンスを展開するとメソッドが失われるということです。

```ts:TypeScript
class C {
  p = 12;
  m() {
  }
}
let c = new C();
let clone = { ...c };
clone.p; // OK
clone.m(); // エラー!
```

2 つ目の制約は、TypeScript コンパイラではジェネリックメソッドの型引数を展開できないことです。
この機能は将来の言語規格に含まれると思われます。