今まで空のオブジェクト{}
として推論されていたのですが、TypeScript3.5から導入されたunknown
が推論に使用されるようになりました。
タイプ定義が無いライブラリ等を使用している場合、今まで通ってたコンパイルが結構通らなくなる可能性が高いです。
私もかなりの数のエラーが発生しました。
unknown
型について
{}
とunknown
は似ておりますが、以下の違いがあります。
-
{}
は文字列でインデックス参照ができます(例:k["foo"]
)が、unknown
はできません -
{}
はnull
やundefined
ではないとみなされるが、unknown
はnull
やundefined
の可能性があります -
{}
はオブジェクトに代入可能ですが、unknown
はできません
対策
例として、型定義がないライブラリのフック関数等を定義する場合を考えます。
// 今まで型が未定義の場合は以下のように`{}`と推論されていた
// type HookType = (args: {}) => void;
// 3.5からは`unknown`型と推論される
type HookType = (args: unknown) => void;
const hook: HookType = args => {
// error: Object is of type 'unknown'.
console.log(args.hoge);
};
対策としてはまず、無理やりキャストする方法があります。
const hook: HookType = args => {
const castedArgs = args as { hoge: any };
// ok
console.log(typedArgs.hoge)
}
ただ、無理やりキャストするよりもUser-Defined Type Guards
を使いましょう。
https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards
返り値の型に引数名 is 型
のように指定することでタイプガードを定義することができます。
これを使って条件式を書くことで、型の推論を限定する(narrow)ことができます。
const hasHogeProperty(args: any): args is { hoge: any } => {
if (!data) return false;
if (data.hoge) {
return true;
}
return false;
}
const hook: HookType = args => {
if (hasHogeProperty(args)) {
// ok
console.log(args.hoge)
}
}