TypeScript

[TypeScript] String Literal Typesで無理やりURL型を実装する

More than 1 year has passed since last update.

以前、文字列型のバグをできるだけ埋め込みたくないという記事を書きました。

最近になってUser-Defined Type Gurdsの意味を理解しまして、
これとString Literal Typesを使ったら、スマートに解決しそうだなと思ったので実装してみます。

実装

type URLString = 'isURLFormat関数でチェックされた文字列';

function isURLFormat(str: string): str is URLString {
    return /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- ./?%&=]*)?/.test(str);
}

こう。
文字列リテラル型で適当に定義しておくのと、それからType Gurdsで正規表現をパスしたらその型として扱うということにします。

使用例

interface RequestOptions {
    url: URLString;
}

function buildOptions(target: string): RequestOptions {
    return { url: target };
}

このように、文字列をノーチェックで使おうとすると……

error TS2322: Type '{ url: string; }' is not assignable to type 'RequestOptions'. Types of property 'url' are incompatible.
Type 'string' is not assignable to type '"isURLFormat関数でチェックされた文字列"'.

コンパイルエラーになります。
このエラーを回避して正常にコンパイルを通すためには、

function buildOptions(target: string): RequestOptions {
    if (isURLFormat(target)) {
        // このスコープではtargetはURLString型
        return { url: target };
    } else {
        // ここで何かしらの例外処理
        throw new Error('Invalid URL')
    }
}

このようにチェックをきちんと記述する必要が出てくるわけです。

利点

  • 文字列の形式がチェックされているかどうかを型で明示できる
  • コンパイル後のJSではただの文字列である
    • 無駄なオブジェクトとかができない
    • 既存の文字列を受け取る関数とかにそのまま渡せる

欠点

  • String Literal Typesってそういう使い方するものじゃない。きもい。

多分つかわない。なんかこう、こういうの用の型定義の書き方があればいいんですけれど。