[TypeScript] `any non-nullish value`としての`{}`型

TypeScriptでプログラミングしていると`{}`型をGenericsの制約とかで使おうとして、よくeslintに怒られます。

```ts
type Foo<T extends {}> = { [KEY in keyof T]: Bar<T[KEY]> };
// Don't use `{}` as a type. `{}` actually means "any non-nullish value".
// - If you want a type meaning "any object", you probably want `Record<string, unknown>` instead.
// - If you want a type meaning "any value", you probably want `unknown` instead.
// - If you want a type meaning "empty object", you probably want `Record<string, never>` instead.eslint@typescript-eslint/ban-types
```

曰く

> 型として `{}` を使用しないでください。実際には `{}` は「任意の非ヌル値」を意味します。
> - もし「任意のオブジェクト」を意味する型なら、多分`Record<string, unknown>`が代わりになるでしょう。
> - もし「任意の値」を意味する型なら、多分`unknown`が代わりになるでしょう。
> - もし「空のオブジェクト」を意味する型なら、多分`Record<string, never>`が代わりになるでしょう。 eslint@typescript-eslint/ban-types

しかし、警告に書いてある、`any non-nullish value`としての`{}`型を使おうとしたとき、ここに上げられている`Record<string, unknown>`や`unknown`、`Record<string, never>`では代わりになりませんでした。

`any non-nullish value`としての`{}`型の代わりには何を使えば良いのでしょうか?

## TL; DR

`Record<never, never>`が使えました。

```ts
type D<T extends Record<never, never>> = T;
type Dundefined = D<undefined>;
// -> 型 'undefined' は制約 'Record<never, never>' を満たしていません。
type Dnull = D<null>;
// -> 型 'null' は制約 'Record<never, never>' を満たしていません。
type Dtrue = D<true>;
type Dfalse = D<false>;
type D1 = D<1>;
type Dabc = D<'abc'>;
type Darray = D<[]>;
type Dempty = D<{}>;
type Dobject = D<{ abc: true }>;
type Diterator = D<{ [Symbol.iterator]: true }>;
```

Recordの第2引数は`never`でなくとも、`string`でも`unknown`でもOKなので、要はkeyがない(`never`)ってことのようです。

## `{}`の代わりに使ってみる

eslintの警告にあるように`Record<string, unknown>`や`unknown`、`Record<string, never>`を`{}`の代わりに使ってみます。

まずは元の`{}`を使ったもの。

```ts
type O<T extends {}> = T;
type Oundefined = O<undefined>;
// -> 型 'undefined' は制約 '{}' を満たしていません。
type Onull = O<null>;
// -> 型 'null' は制約 '{}' を満たしていません。
type Otrue = O<true>;
type Ofalse = O<false>;
type O1 = O<1>;
type Oabc = O<'abc'>;
type Oarray = O<[]>;
type Oempty = O<{}>;
type Oobject = O<{ abc: true }>;
type Oiterator = O<{ [Symbol.iterator]: true }>;
```

`any non-nullish value`、つまり`undefined`と`null`だけを除外する制約になっています。

### `Record<string, unknown>`

```ts
type A<T extends Record<string, unknown>> = T;
type Aundefined = A<undefined>;
// -> 型 'undefined' は制約 'Record<string, unknown>' を満たしていません。
type Anull = A<null>;
// -> 型 'null' は制約 'Record<string, unknown>' を満たしていません。
type Atrue = A<true>;
// -> 型 'boolean' は制約 'Record<string, unknown>' を満たしていません。
type Afalse = A<false>;
// -> 型 'boolean' は制約 'Record<string, unknown>' を満たしていません。
type A1 = A<1>;
// -> 型 'number' は制約 'Record<string, unknown>' を満たしていません。
type Aabc = A<'abc'>;
// -> 型 'string' は制約 'Record<string, unknown>' を満たしていません。
type Aarray = A<[]>;
// -> 型 '[]' は制約 'Record<string, unknown>' を満たしていません。
//      型 'string' is missing in type '[]' のインデックス シグネチャがありません。
type Aempty = A<{}>;
type Aobject = A<{abc: true}>;
type Aiterator = A<{[Symbol.iterator]: true}>;
```

オブジェクト型以外の型を除外(配列も除外)するには役に立ちそうですが、`any non-nullish value`ではないですね。

### `unknown`

```ts
type B<T extends unknown> = T;
type Bundefined = B<undefined>;
type Bnull = B<null>;
type Btrue = B<true>;
type Bfalse = B<false>;
type B1 = B<1>;
type Babc = B<'abc'>;
type Barray = B<[]>;
type Bempty = B<{}>;
type Bobject = B<{ abc: true }>;
type Biterator = B<{ [Symbol.iterator]: true }>;
```

`extends unknown`では何の制約にもなりませんが、分かりやすいように明示的に書いておきます。

まあ書いたように何の制約にもならないのですべてエラー無しです。`{}`の代わりには使えません。

eslintの警告にもあるように任意の型として使うなら、ということなのでしょう。

### `Record<string, never>`

```ts
type C<T extends Record<string, never>> = T;
type Cundefined = C<undefined>;
// -> 型 'undefined' は制約 'Record<string, never>' を満たしていません。
type Cnull = C<null>;
// -> 型 'null' は制約 'Record<string, never>' を満たしていません。
type Ctrue = C<true>;
// -> 型 'boolean' は制約 'Record<string, never>' を満たしていません。
type Cfalse = C<false>;
// -> 型 'boolean' は制約 'Record<string, never>' を満たしていません。
type C1 = C<1>;
// -> 型 'number' は制約 'Record<string, never>' を満たしていません。
type Cabc = C<'abc'>;
// -> 型 'string' は制約 'Record<string, never>' を満たしていません。
type Carray = C<[]>;
// -> 型 '[]' は制約 'Record<string, never>' を満たしていません。
//      型 'string' is missing in type '[]' のインデックス シグネチャがありません。
type Cempty = C<{}>;
type Cobject = C<{ abc: true }>;
// -> 型 '{ abc: true; }' は制約 'Record<string, never>' を満たしていません。
//     プロパティ 'abc' はインデックス シグネチャと互換性がありません。
//       型 'boolean' を型 'never' に割り当てることはできません。
type Citerator = C<{ [Symbol.iterator]: true }>;
```

空のオブジェクト`{}`だけが通るのかと思いきや、シンボルを指定した`{ [Symbol.iterator]: true }`も通ってしまいました。

シンボルで指定されたプロパティも除外するなら`Record<string|symbol, never>`とするべきでしょうか?

ついでなのでkeyとして指定できる`number`も付け加えておきましょう。

```ts
type C2<T extends Record<string | symbol | number, never>> = T;
type C2undefined = C2<undefined>;
// -> 型 'undefined' は制約 'Record<string | symbol | number, never>' を満たしていません。
type C2null = C2<null>;
// -> 型 'null' は制約 'Record<string | symbol | number, never>' を満たしていません。
type C2true = C2<true>;
// -> 型 'boolean' は制約 'Record<string | symbol | number, never>' を満たしていません。
type C2false = C2<false>;
// -> 型 'boolean' は制約 'Record<string | symbol | number, never>' を満たしていません。
type C21 = C2<1>;
// -> 型 'number' は制約 'Record<string | symbol | number, never>' を満たしていません。
type C2abc = C2<'abc'>;
// -> 型 'string' は制約 'Record<string | symbol | number, never>' を満たしていません。
type C2array = C2<[]>;
// -> 型 '[]' は制約 'Record<string | symbol | number, never>' を満たしていません。
//      型 'string' is missing in type '[]' のインデックス シグネチャがありません。
type C2empty = C2<{}>;
type C2object = C2<{ abc: true }>;
// -> 型 '{ abc: true; }' は制約 'Record<string | symbol | number, never>' を満たしていません。
//     プロパティ 'abc' はインデックス シグネチャと互換性がありません。
//       型 'boolean' を型 'never' に割り当てることはできません。
type C2iterator = C2<{ [Symbol.iterator]: true }>;
// -> 型 '{ [Symbol.iterator]: true; }' は制約 'Record<string | number | symbol, never>' を満たしていません。
//     プロパティ '[Symbol.iterator]' はインデックス シグネチャと互換性がありません。
//       型 'boolean' を型 'never' に割り当てることはできません。
```

`{}`以外は除外できるようになりました。それに何か意味があるのかどうかは分かりませんが。

`any non-nullish value`としての`{}`型の代わりにはなりませんでした。

## 空のオブジェクトとは?

ここで初心に戻って`{}`、つまり空のオブジェクトとは何なのか考えてみます。

空のオブジェクト、`{}`とは、プロパティを1つも持たないもの、です。

通常のオブジェクトは、`{ [KEY in keyof T]: T[KEY] }`で表せるように`keyof T`が存在していますが、空のオブジェクトにはそれが存在していないので、`keyof T`が何もない、つまり`never`となります。

```ts
type KEYS<T> = keyof T;
type KEYSEMPTY = KEYS<{}>;
// -> type KEYSEMPTY = never;
```

ここから逆算して型を作ってみると

```ts
type EMPTYOBJECT = { [KEY in never]: unknown };
```

となります。

Recordを使って書きなおせば

```ts
type EMPTYOBJECT = Record<never, unknown>;
```

となります。

プロパティの型は存在していないので何でも構いません。どうせならneverを強調して

```ts
type EMPTYOBJECT = Record<never, never>;
```

としておきましょう。

### 使ってみる

```ts
type D<T extends Record<never, never>> = T;
type Dundefined = D<undefined>;
// -> 型 'undefined' は制約 'Record<never, never>' を満たしていません。
type Dnull = D<null>;
// -> 型 'null' は制約 'Record<never, never>' を満たしていません。
type Dtrue = D<true>;
type Dfalse = D<false>;
type D1 = D<1>;
type Dabc = D<'abc'>;
type Darray = D<[]>;
type Dempty = D<{}>;
type Dobject = D<{ abc: true }>;
type Diterator = D<{ [Symbol.iterator]: true }>;
```

ちゃんと`undefined`と`null`だけ除外できました。`any non-nullish value`としてはこうなってほしいですね。

みんな大好き(?)[type-challenges](https://github.com/type-challenges/type-challenges)で使われている`Equal`で比較しても一致します。

```ts
type TEST = Equal<{}, Record<never, never>>;
// -> type TEST = true;
```

やはり、`{}`をそのままの意味、`any non-nullish value`として置き換えるなら`Record<never, never>`を使うのが良さそうです。