書こうと思ったらなんかあいてたのでついでに埋めときます。
function test(a: string[] | readonly number[] | {bbb: string})
こんな関数があったと思ってください。なんだこの引数aの型は、とかは思っても気にしない。
とりあえず、bbbプロパティを使った処理をしたいので、Array.isArray
で配列型を除外しようと思います。
if (!Array.isArray(a)) {
// これでこの中ではaを`{bbb:string}`型として扱えるはず
}
意気揚々とbbbにアクセスしようとしたら
if (!Array.isArray(a)) {
a.bbb.slice(0); // Error!!! あれっ!?bbbにアクセスできない?
}
bbbにアクセスするところでエラーになりました。なんで?
TL; DR
標準のArray.isArrayの宣言ではreadonlyな配列に対応していないようです。
declare global {
interface ArrayConstructor {
isArray(arg: unknown): arg is readonly unknown[];
}
}
を追加すると期待通りになりました。
解決の経緯
a.bbbのところのaにカーソルを合わせて型を確認すると…
(parameter) a: readonly number[] | {
bbb: string;
}
readonly number[]
が消えてません。
Array.isArrayの宣言を確認すると
interface ArrayConstructor {
// ...
isArray(arg: any): arg is any[];
// ...
}
となっていました。
any[]はreadonlyではないのでreadonly number[]は除外してくれないようです。
仕方ないので以下を追加しました。
declare global {
interface ArrayConstructor {
isArray(arg: unknown): arg is readonly unknown[];
}
}
こうすると
function test(a: string[] | readonly number[] | {bbb: string}) {
if (!Array.isArray(a)) {
a;
// ここでaは
// {bbb: string;}
}else{
a;
// ここでaは
// string[] | readonly number[]
}
}
となって期待通りになります。
unknown
もなかった頃の宣言がそのまま残ってるとかなんですかね?