TypeScriptで正規表現を用いて文字列解析(match)をする場合にobject is possibly 'null'を回避する方法がちゃんと解説されてるもの少なかったので、備忘録的にメモ
まず、regexp.matchの動作の正しい理解をする。
- 条件にマッチするとArrayが返る
- 条件にマッチするものがないとNullが返る
- gフラグない場合、Arrayの中は以下のようになる
['最初に完全一致したもの','キャプチャグループ','追加プロパティ]
[**String.prototype.match()の説明ページ**](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/match)
_返り値:Array を返します。これはグローバル (g) フラグの有無によって内容が変わります。 一致するものが見つからなかった場合は null を返します。_
_g フラグがあった場合は、正規表現全体に一致したすべての結果を返しますが、 キャプチャグループは返しません。_
_g フラグがなかった場合、最初に完全に一致したものと、それに関するキャプチャグループを 返します。この場合、返される要素には下記の追加のプロパティが存在します。_
例えば以下のような記述ではobjedt is possibly 'null'が出る。
const regexp = "http://hogehoge.com/?limit=10&token=XXXXXXX";
const limit = regexp.match(/limit=(.*?)(&|$)/)[1];
正規表現でマッチさせたキャプチャ文字を変数に代入しようとしている。
この時、regexp.matchがマッチしなかった場合nullが返ってくる。
これだと、Nullabilityエラーが出てしまう。
そこで、Nullabilityを完全に除去するには、以下のようにする事がまず思い浮かぶ。
const regexp = "http://hogehoge.com/?limit=10&token=XXXXXXX";
const _limit = regexp.match(/limit=(.*?)(&|$)/);
let limit = 30; // デフォルト設定として合う買う
if ( _limit !== null) {
limit = Number(_limit[1])
}
ちなみに、_limit[1] as number
みたいなダウンキャストでは怒られてしまった。あまり深く考えてなかったけど、ダウンキャストは型安全じゃないとかっていう事位の把握なので、今度調べる。
いずれにせよ上記だと非常に冗長。
そこでより完結(?ワンライン)に書いてみた。
const regexp = "http://hogehoge.com/?limit=10&token=XXXXXXX";
const limit = Number((regexp.match(/limit=(.*?)(&|$)/) ?? '')[1] || 20);
const token = (regexp.match(/token=(.*?)(&|$)/) ?? '')[1];
ポイントとしてはregexp.matchがマッチすればArrayを返し、外側の[1]
の式が評価され、array[1]
となり、nullを返した場合、''(空文字)
を返すようになっている。
空文字が返ると、次の評価式は''[1]
となる。これは文字列に配列添字でアクセスする事と同意で、'abcde'[2]
とかと同じ事を表してる。この場合、abcdeを0,1,2,3...の添字でアクセスしているので、c
が返ってくる。
では、文字列の範囲外を添字指定するとどうなるか?
この場合、undefined
が返る。これにより、nullが返ってきた場合、undefinedとして評価され、||
オペレータでの評価により、最後の|| 20
が評価され、limitにはデフォルト値の20が設定される。
さらに、limitの下のtokenではデフォルト値を設定せず、マッチがなければtokenにはundefinedをそのまま入れてる。これをそのまま次の関数の引数等に渡してやると、関数がtokenをオプション等で受け取るようになっていれば、関数実行時に適切に実行される。
const regexp = "http://hogehoge.com/?limit=10&token=XXXXXXX";
const limit = Number((regexp.match(/limit=(.*?)(&|$)/) ?? '')[1] || 20);
const token = (regexp.match(/token=(.*?)(&|$)/) ?? '')[1];
const res = afterMethod(limit, token)
※ tokenに値が入ってる場合: afterMethod(limit, token)
※ tokenがundefinedの場合: afterMethod(limit)