TypeScript オブジェクトを結合したいなら、shallow copy か lodash.merge で？

# 背景
DynamoDB に対する CommandInput を、条件によって切り替えたかった。

イメージとしては、以下の FilterExpression を On/Off したかった
```typescript: Expression
const params = {
    ExpressionAttributeNames: {
        "#PK": uid,
        "#timestamp": "timestamp"
    },
    ExpressionAttributeValues: {
        ":PK": userID,
        ":sortkeyval1": startDate.getTime(),
        ":sortkeyval2": endDate.getTime()
    },
    KeyConditionExpression: "#PK = :PK"
    FilterExpression: "#timestamp BETWEEN :sortkeyval1 AND :sortkeyval2"
}
```

# 結論
## スプレッド構文を使って、浅いコピーを利用する
merge する部分が微妙な感じになってしまうけど、まぁ、ライブラリも使わなくて済む
```typescript: merge
const keyCondition = {
    ExpressionAttributeNames: {
        "#PK": "uid",
    },
    ExpressionAttributeValues: {
        ":PK": userID,
    },
    KeyConditionExpression: "#PK = :PK",
}
const filterExpression = {
    ExpressionAttributeNames: {
        "#timestamp": "timestamp"
    },
    ExpressionAttributeValues: {
        ":sortkeyval1": startDate.getTime(),
        ":sortkeyval2": endDate.getTime(),
    },
    FilterExpression: "#timestamp BETWEEN :sortkeyval1 AND :sortkeyval2"
}
const params = (withFilter)? 
    {...keyCondition, ...filterExpression, ExpressionAttributeNames: {...keyCondition.ExpressionAttributeNames, 
        ...filterExpression.ExpressionAttributeNames}, ExpressionAttributeValues: {...keyCondition.ExpressionAttributeValues, ...filterExpression.ExpressionAttributeValues}}:
    keyCondition;
```

## lodash を使う場合
```typescript: merge by lodash
import _ from "lodash";
const paramsMerged =  (withFilter)? 
    _.merge(keyCondition, filterExpression):
    keyCondition;
```

## 結果
どちらも以下になる。分かりやすさとしては lodash でしょうねぇ
```typescript: merged result
{
  ExpressionAttributeNames: { 
    '#PK': 'uid', 
    '#timestamp': 'timestamp' 
  },
  ExpressionAttributeValues: {
    ':PK': 'uid',
    ':sortkeyval1': 1672148172970,
    ':sortkeyval2': 1672148172970
  },
  KeyConditionExpression: '#PK = :PK',
  FilterExpression: '#timestamp BETWEEN :sortkeyval1 AND :sortkeyval2'
}
```

# スプレッド構文の試行
スプレッド構文自体の理解の為に、色々試してみた

```typescript: 
var content1 = {
    id: "1",
    timestamp: 123
  }
var content2 = {
  uid2: "2"
};
var content3 = {
  id: "3"
};
var value = "4";
var value2 = "5";
var content4 = {
  type: {
    audience: "people",
  }
};
var content5 = {
  type: {
    audience: "animal",
    author: "person",
    batchSize: 5,
  },
  id: 1,
}

console.log({value, ...content1}, "変数とオブジェクト");
console.log({value, ...content1, value2}, "変数とオブジェクト2");
console.log({nameChanged: value, ...content1}, "変数に名前を付けて");
console.log({...{value}, ...content1, }, "オブジェクトにして");
console.log({...{nameChanged: value}, ...content1, }, "オブジェクトにして名前を付けて");
console.log({...content2, ...content1, }, "オブジェクト同士を");
console.log({...{content2}, ...content1, }, "さらにオブジェクト化");
console.log({...{nameChanged: content2}, ...content1, }, "オブジェクトの名前を付けて");

console.log({...content2, ...content1, ...content3, }, "同一プロパティの上書き id = 1 / 3");

console.log({...content5, ...content4, type: {...content5.type, ...content4.type}}, "プロパティにオブジェクトがある場合");
```

## 結果
プロパティ名を変更しながら Merge したい時や、変数を組み込みつつって時に便利そう。
一番最後のが、上の結論に利用したプロパティ単位での浅いコピー(shallow copy)
```typescript: result
{ value: '4', id: '1', timestamp: 123 } // 変数とオブジェクト
{ value: '4', id: '1', timestamp: 123, value2: '5' } // 変数とオブジェクト2
{ nameChanged: '4', id: '1', timestamp: 123 } // 変数に名前を付けて
{ value: '4', id: '1', timestamp: 123 } // オブジェクトにして
{ nameChanged: '4', id: '1', timestamp: 123 } // オブジェクトにして名前を付けて
{ uid2: '2', id: '1', timestamp: 123 } // オブジェクト同士を
{ content2: { uid2: '2' }, id: '1', timestamp: 123 } // さらにオブジェクト化
{ nameChanged: { uid2: '2' }, id: '1', timestamp: 123 } // オブジェクトの名前を付けて
{ uid2: '2', id: '3', timestamp: 123 } // 同一プロパティの上書き id = 1 / 3
{ type: { audience: 'people', author: 'person', batchSize: 5 }, id: 1 } // プロパティにオブジェクトがある場合
```

# あとがき
スプレッド構文を知らなかったけれど、オブジェクトを結合ってだけで以下に辿り着けて助かりました。感謝

https://typescriptbook.jp/tips/merge-objects