お互いの type を union type としますが、お互いの property を never にしたいとき

## はしがき
仕事をしながら、api 取得からこのような jbuilder をみました。
```ruby
if @hello?
  json.hello 1
  json.world hello_world
  return
end

json.ohaiyo 'asa'
json.sekai ohaiyo_sekai
```

このような感覚なので、typescript は以下のように書きました。

```typescript
interface Hello {
  hello: number
  world: object
}
interface Sekai {
  ohaiyo: string
  sekai: object
}
type ApiResponse = Hello | Sekai
```

単純に `A|B` しても問題ありません。
しかし、以下のようなコードは許されたので、もっと完璧に許可したくなくて、細かく設定するようにしました。

https://www.typescriptlang.org/play/?ssl=15&ssc=2&pln=11&pc=1#code/JYOwLgpgTgZghgYwgAgBIQDYYPbIN4CwAUMsgBaY4BcyIArgLYBG0xpA7tlBgCY3ZMAVhARhiAX2KhIsRCgDKEANZxg+NsmxlVAT2w0AzmCigA5hoPLV-ISLFFJRMDoAOKAIIvgAJQgGX2CCWyAC8aJS4AD7IiirAxMQIgUbIkEYAanAYdChhhCTkETQAjAA0GpzcfPji5QVauvrIAORpYM0SQA

```typescript
interface Hello {
  hello: number
  world: object
}
interface Sekai {
  ohaiyo: string
  sekai: object
}
type ApiResponse = Hello | Sekai

const testValue: ApiResponse = {
  hello: 1,
  world: {},
  ohaiyo: 'test' // 人の観点ではありえない property です。
}
```

## 目的

1. union する type1, type2 のお互いの property を許可しないこと

## 結果


```typescript
type PreventUnnessesaryPropertiesOf<T> = {
    [K in keyof T]: Record<K, never> &
        Partial<Record<Exclude<keyof T, K>, never>>;
}[keyof T];

type MutualAssuredExclude<T, K> =
    | T
    | K
    | PreventUnnessesaryPropertiesOf<T & K>;
```

お互いの type の property を optional の never と設定、その上に自分の type の property を定義する、これを union type として、設定すれば、ok

```typescript
interface Hello {
  hello: number
  world: object
}
interface Sekai {
  ohaiyo: string
  sekai: object
}

type PreventUnnessesaryPropertiesOf<T> = {
    [K in keyof T]: Record<K, never> &
        Partial<Record<Exclude<keyof T, K>, never>>;
}[keyof T];

type MutualAssuredExclude<T, K> =
    | T
    | K
    | PreventUnnessesaryPropertiesOf<T & K>;

type MutualAssuredApiResponse = MutualAssuredExclude<Hello, Sekai>

const testValue: MutualAssuredApiResponse = {
  hello: 1,
  world: {},
  ohaiyo: 'test' // error になる
}

const testValue2: MutualAssuredApiResponse = {
  hello: 1,
  ohaiyo: 'test', // error になる
  sekai: {}
}
```

## 解決方法

generic type を A, B 取得したら A の場合は B の property を never、B　の場合は A の property を never と設定することで解決できると思った。

```typescript
type MutualAssuredExclude<T, P> = 
  | ({ [P in keyof K]: never } & T) // T を使う時 P の property を never
  | ({ [P in keyof T]: never } & P) // P を使う時 T の property を never
```

### optional ではないので、never の property が必ず必要

never と設定した property は必ず宣言する必要がある、しかし、それは never なので、宣言したらエラーが表示される

なので、以下のように optional に設定してエラーがならないようにしました。

```typescript
type MutualAssuredExclude<T, P> = 
  | ({ [P in keyof P]?: never } & T)
  | ({ [P in keyof T]?: never } & P)

const testValue: MutualAssuredExclude<Hello, Sekai> = {
  ohaiyo: 'test',
  sekai: {}
}
```

### property が重複になったら、エラー

なんか、急に気になって、「property が重複になったら、どうするか？」なので、テストしました。

https://www.typescriptlang.org/play/?#code/JYOwLgpgTgZghgYwgAgBIQDYYPbIN4CwAUMsgBaY4BcyIArgLYBG0xpA7tlBgCY3ZMAVhARhiAX2KhIsRCgDKEANZxg+NsmxlVAT2w0AzmCigA5hoPLV-ISLFFJRMDoAOKALJ0wdOBgCCBgZ0UBA8AKIAHggYdDwQADwAKgA0yADSAHzIALzIGgA+yAAUeMgA2gAKyKDIShB6MOkAugD8NCAQAG7QyOLIAGTIiQCUBcWlldUgtfXYjYmt7V09fYNpo0TECNggRsiQRgBqvnQQNJ7evgFBIeFRMXHx6FjYqYoqwFm5hCSa2sB6GgAcgOYCByQsVmANDwjkcUnA0HgSGQfnUvzg7UYLCgGiYWOYrF+CAJOIkCJkyJQACF0aQSbRsUTSHxGYTcb8zmyyQ5iFsdntQccYhAAEznLw+fyBYKhSLRWIJPypalfOnITHIACMAGYIb98dq9RoGbr9Y4gA

```typescript
type MutualAssuredExclude<T, P> = 
  | ({ [P in keyof P]?: never } & T)
  | ({ [P in keyof T]?: never } & P)
interface A {
  a: number
  b: number
  c: number
}
interface B {
  c: number
  d: number
  e: number
}

const testValue: MutualAssuredExclude<A, B> = {
  a: 13, // error
  b: 13, // error
  c: 13, // error
}
```

property a, b, c が全部 never と言われました。
これ少し考えたら、`{a: number, b: number, c: number} & {c?: never, d?: never, e?: never}` は c が never になるので、できないはずかなと思いました。

なので、union type を使って、お互いの type を使えなくなるようにしました。

```typescript
type MutualAssuredExclude<T, P> = 
  | ({ [P in keyof P]?: never } | T)
  | ({ [P in keyof T]?: never } | P)
```

さっきの A, B を使ったら、`({ [P in keyof P]?: never } | T)` この部分は `{a: number, b: number, c: number} | {c?: never, d?: never, e?: never}` になって、property a, b, c を使う時、 d, e を使ったら、never なので、エラー、また、optional condition なので、使わなくてもエラーはない

テストの結果：
https://www.typescriptlang.org/play/?#code/JYOwLgpgTgZghgYwgAgBIQDYYPbIN4CwAUMsgBaY4BcyIArgLYBG0xpA7tlBgCY3ZMAVhARhiAX2KhIsRCgDKEANZxg+NsmxlVAT2w0AzmCigA5hoPLV-ISLFFJRYmB0AHFAFk6YOnAwBBAwM6KAgeAFEADwQMOh4IAB4AFQAaZABpAD5kAF5kDQAfZAAKPGQAbQAFZFBkJQg9GAyAXQB+GhAIADdoZHFkIqSASkKSsqqakDqG7Cakto7u3v6i9JGnIhd3ZC8fP0Dg0J5-V2AAJQgDV2wQS1yd718AoJCwqJi4xPQsbDTFFWAmWIxAQNyMyEgRgAan46BAaLsngdXsdThcrmCUHlCCRyJR9MgAIwpDScbh8fDiEm4rS6AkAckhYHpEmBRFBtzAEMuYBhsQgACYEY99i8jidzpdrrcsepcRQfjRiRpacA9DRGTz6dTSJYATQ8I5xEA

といっても、全部 or なっているので、簡潔に書けるかも

```typescript
type MutualAssuredExclude<T, P> = 
  | { [P in keyof K & T]?: never } | T | P
```

全部 or だったのでこのように書いて ok です。しかし、`{ [P in keyof P & T]?: never }` これが `{}` を許可しますので、予想通りに動いていません。

### 一つずつエラーになる type をつくる

https://qiita.com/dkdidudtn/items/56f1565b6048ff6091ec#comment-683d7deb74cc7e67f141

のおかげさまで、気づきました。

たしかに、`{}` も許可しているので、解決のながれをみながら、ロジックを考え直しました。

- ロジックの要件
1. generic T と P をもらう
1. T と P だけのときは許可(`T | P`)
1. T もしくは P の property が一緒にある時は `never` にすること

1と2はすでに適用しています。
3は T のとき P の property は never
P のとき T の property は never

```typescript
// hardcode 例
type HardcodePreventUnnessesaryProperties =
  | { T1: never, T2?: never, T3?: never,...}
  | { T1?: never, T2: never, T3?: never,...}
  ...
  | { P1: any, P2: any, P3: any... } // P の事態は許可しますなので
```

であれば、お互いの property を全体 never にしましょう。
ある property は never にしますが、ほかの property は optional する必要があります。

```typescript
// 1. 各 property を never にする
// 2. 各 property 以外の property は optional never にする

type PreventUnnessesaryPropertiesOf<T> = {
    [K in keyof T]: Record<K, never> &
        Partial<Record<Exclude<keyof T, K>, never>>;
}[keyof T];

// もしくは

type PreventUnnessesaryPropertiesOf<T> = {
    [K in keyof T]: { K: never } &
        Partial<{[L in Exclude<keyof T, K>]: never}>;
}[keyof T];

// comment で助言いただいたことをカッコで書いたら、どうかして、書いてみました。
```

これで、T もしくは P のお互いの property を一緒に使うときはエラーになります。

```typescript
type MutualAssuredExclude<T, K> = 
  | PreventUnnessesaryPropertiesOf<T>
  | T
  | PreventUnnessesaryPropertiesOf<K>
  | K
// また、これは union なので、以下のようにできます。
type MutualAssuredExclude<T, K> = 
  | PreventUnnessesaryPropertiesOf<T & K>
  | T
  | K
```

### typescript 5.1 以前の version では？

5.1.6 playground

https://www.typescriptlang.org/play/?ts=5.1.6#code/C4TwDgpgBACgThAbhAdsAqilEDOPcCGcI8A9pHMAJa4DyAZgDwAqAfFALxQDeAUFAKgBtANJQqKKAGsIIUvSjMAugC4oAJQgBjUnAAmjEQBoo2ZHHYAyfoNswi1AgBtGmnfsYBRAB5anAVz0IRhk5BWYTEVYTMwgLVl4AX15eUEgoAFk4gHMIADkkOOZwYIjYdi4+W1FxSVD5RVVFUSUoS1gkoXrwpVSSzP9gf2cAQTx-BD0fP0DSkxgKqBsAH0yc-MK4YshGeEK0TGw8QmIyCmo6JjZ5hIFVrLhcgvNt4L3kA6xcfBwiEjhyHELjgGLtoopblBVswVh1eBJgHF6AQtNARjwbAICGoUP4ALYAIzimKgBJx+KJcBJWnJhOJyXhaCRKOgACEMbYAPScqA00wU4m2PS0ykkiAiwWCegSqkMnQoHDAKCIxUANWc-nFAyGo3Gk2mASCjBGJlZiyqgj5ABYjCThVAAMy22xap0k6WO22JIA

5.0.4 playground

https://www.typescriptlang.org/play/?ts=5.0.4#code/C4TwDgpgBACgThAbhAdsAqilEDOPcCGcI8A9pHMAJa4DyAZgDwAqAfFALxQDeAUFAKgBtANJQqKKAGsIIUvSjMAugC4oAJQgBjUnAAmjEQBoo2ZHHYAyfoNswi1AgBtGmnfsYBRAB5anAVz0IRhk5BWYTEVYTMwgLVl4AX15eUEgoAFk4gHMIADkkOOZwYIjYdi4+W1FxSVD5RVVFUSUoS1gkoXrwpVSSzP9gf2cAQTx-BD0fP0DSkxgKqBsAH0yc-MK4YshGeEK0TGw8QmIyCmo6JjZ5hIFVrLhcgvNt4L3kA6xcfBwiEjhyHELjgGLtoopblBVswVh1eBJgHF6AQtNARjwbAICGoUP4ALYAIzimKgBJx+KJcBJWnJhOJyXhaCRKOgACEMbYAPScqA00wU4m2PS0ykkiAiwWCegSqkMnQoHDAKCIxUANWc-nFAyGo3Gk2mASCjBGJlZiyqgj5ABYjCThVAAMy22xap0k6WO22JIA

また、助言をいただきましたが、

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-1.html#negative-case-checks-for-union-literals

性能のために5.1からできそうです。