3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

TypeScriptAdvent Calendar 2021

Day 6

[TypeScript] Array.isArrayでReadonlyArrayの型ガード

Posted at

書こうと思ったらなんかあいてたのでついでに埋めときます。

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もなかった頃の宣言がそのまま残ってるとかなんですかね?

3
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?