unknown 型

anyと似ていますがタイプセーフな型です。型を特定するまであらゆる操作が制限されるみたいな型です。unknownは存在しうるすべての型の Union のようなものかなと思います。(anyも)

const foo: unknown = 'foo';
// これは以下のようなもの(`IDO5`などは適当)
const foo: IDO5 | n0lM | 'sowe' | ... | string | number | boolean | undefined | null = 'foo'

上記では'foo'を入れていますが、これは Union にstringもしくは'foo'型が含まれる(イメージ)ので代入することができます。

ですがunknownの特徴として、unknownのままではプロパティやプロトタイプメソッドなどすべてのアクセスが制限されてしまいます。

const a: unknown = 'a';
a.foo
// Object is of type 'unknown'.

型を特定する

そのまま使えるのならそのままでいいかもしれないですが、unknownで何かしようと思ったら使う前にまず型変換してあげる必要があります。

例えばstring | undefinedという型valueの時、if (value !== undefined) {...}とすることで...ではvaluestring型だということに絞り込むことができます。

「存在しうるすべての型の Union のようなもの」と言ったようにunknown肯定的if文などで特定の型にキャストすることができます。また他の同じようにtypeofinstanceof,inなどを使った絞り込みも効いてくれます。

例えば以下のようにunknown型をstringに絞り込めます。

const a: unknown = 'a';
// const a: unknown
if (typeof a === 'string') {
  // const a: string
}

タイプガードの場合。これはFooBarに絞り込みます。

interface Foo {
  foo: string;
}
interface Bar {
  bar: string;
}

const isFoo = (value: unknown): value is Foo => {
  return typeof value === 'object' && typeof (value as Foo).foo === 'string';
}

const isBar = (value: unknown): value is Bar => {
  return typeof value === 'object' && typeof (value as Bar).bar === 'string';
}


const a: unknown = {};
if (isFoo(a)) {
  // const a: Foo
} else if (isBar(a)) {
  // const a: Bar
}

使い所

  1. スコープの中で最初からあるわけではないデータ
  2. API をinterfaceで定義していく時に後回しにしたいプロパティの型
  3. 関数のオーバーロード定義時ただ引数の数を合わせたいだけの時の型
  4. 型の種類が多い(そして数が不明かつ API ドキュメントの無いカオスプロジェクト)

みたいな所とかは一旦unknownで置いておいて、使う時になってから実際の型に置き換えたり、キャストしたりするといいかなと思います。

例えばhttp.createServerbody-parserなどの組み合わせで API を実装する時にbodyParser.json()(req, res, next)が終わるまではreq.bodyへのアクセスはさせたくありません。
そのような時に、最初{body: unknown}としておいてbodyParserのミドルウェア部分が終わった後{body: 実際の方}と設定してあげたりするといいかなと考えます。ミドルウェア部分が終わる前のreq.body

あと最近だと強制型変換などで一旦unknownに置いてから目当ての型に再度変換しないと駄目な ESLint ルールとかありしますね。

const a: Foo = {...};
(a as unknown) as Bar