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) {...}
とすることで...
ではvalue
をstring
型だということに絞り込むことができます。
「存在しうるすべての型の Union のようなもの」と言ったようにunknown
は肯定的なif
文などで特定の型にキャストすることができます。また他の同じようにtypeof
やinstanceof
,in
などを使った絞り込みも効いてくれます。
例えば以下のようにunknown
型をstring
に絞り込めます。
const a: unknown = 'a';
// const a: unknown
if (typeof a === 'string') {
// const a: string
}
タイプガードの場合。これはFoo
やBar
に絞り込みます。
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
}
使い所
スコープの中で最初からあるわけではないデータ
API を
interface
で定義していく時に後回しにしたいプロパティの型関数のオーバーロード定義時ただ引数の数を合わせたいだけの時の型
型の種類が多い(そして数が不明かつ API ドキュメントの無いカオスプロジェクト)
みたいな所とかは一旦unknown
で置いておいて、使う時になってから実際の型に置き換えたり、キャストしたりするといいかなと思います。
例えばhttp.createServer
とbody-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