ミリ秒に対する変換を提供するmsパッケージ

## ms
[`ms`](https://github.com/vercel/ms)はVercelが開発するパッケージの1つです。このパッケージでは年や日、時間や分で記述された文字列をミリ秒に変換したりその逆を行うことができます。
ブラウザとNodeの両方で動作し、Edgeランタイムとの互換もあります。

### ミリ秒への変換
例えば1時間のミリ秒を求めたい場合は以下のようにします。
```ts
import ms from 'ms';

// いずれも3600000
ms('1 Hour');
ms('1Hour');
ms('1 HOUR');
ms('1 hour');
ms('1 Hours');
ms('1 Hrs');
ms('1 Hr');
ms('1 H'):
```
`Hour`、`Hours`、`Hrs`、`Hr`、`H`の文字列が数値の後ろに書かれていれば時間を基準として変換してくれます。これらの文字列は全てが大文字もしくは小文字でも同じように解釈されます。そして文字列と数値の間にはスペースがあっても同じようになります。
同じように年や週、日、分、秒がこの形式でミリ秒に変換できます。
```ts
ms('2 years'); // 63115200000
ms('1Week'); // 604800000
ms('10 DAYS'); // 864000000
ms('4 minutes'); // 240000
ms('7 sec'); // 7000
```
注意すべきところはこれらを組み合わせることができないというところです。
```ts
ms('2 years 1week'); // undefined
```
処理できない文字列の場合は`undefined`が返ってきます。
単一のスケールに対応しているので組み合わせたい場合は以下のように足し合わせる必要があります。
```ts
ms('2 years') + ms('1 week'); // 63720000000
```

### ミリ秒からの変換
数値を渡すことで先ほどの逆の変換を行えます。
```ts
ms(11111) // '11s'
```
変換後は一番大きいスケールだけが出力されることに注意してください。上記の例は11s111msを期待してしまいますが、11sが返ってきます。

第2引数にはオプションをオブジェクトで渡すことができます。現在は`long`をキーに真偽値に値として取るオブジェクトを受け取っています。デフォルトは偽です。`true`を渡すことで長い文字列をつけて結果を返します。
```ts
ms(11111, { long: true }) // "11 seconds"
ms(1111, { long: true }) // "1 second"
```
複数形の使い分けをしてくれるのがポイントです。


### おまけ機能？
おまけと言って良いかわかりませんが、数値に変換できる文字列を渡したときは文字列から数値に変換した結果を返してくれます。
```ts
ms('111') // 111
```
`1e10`のような数値として扱える文字列を渡しても数値には変換されません(ミリ秒からの変換で数値型として渡すことはできます)。
```ts
ms('1e10') // undefined
ms(1e10) // 116d
```

### 型による工夫
`ms`は執筆時のバージョン(2.1.3)では型の情報がついてきません。`@types/ms`を用いることで型を付与することはできますが、`ms`関数に`string`もしくは`number`ならなんでも渡せるように定義されています。
```ts
// @types/ms@0.7.34から提供される型
/**
 * Short/Long format for `value`.
 *
 * @param {Number} value
 * @param {{long: boolean}} options
 * @return {String}
 */
declare function ms(value: number, options?: { long: boolean }): string;

/**
 * Parse the given `value` and return milliseconds.
 *
 * @param {String} value
 * @return {Number}
 */
declare function ms(value: string): number;

export = ms;

```
バージョン3からは`ms`から直接型を提供するようになります。現在は安定版としては提供されておらず、`canary`バージョンを利用する必要があります。
```ts
// ms@3.0.0-canary.1から提供される型

declare type Unit = 'Years' | 'Year' | 'Yrs' | 'Yr' | 'Y' | 'Weeks' | 'Week' | 'W' | 'Days' | 'Day' | 'D' | 'Hours' | 'Hour' | 'Hrs' | 'Hr' | 'H' | 'Minutes' | 'Minute' | 'Mins' | 'Min' | 'M' | 'Seconds' | 'Second' | 'Secs' | 'Sec' | 's' | 'Milliseconds' | 'Millisecond' | 'Msecs' | 'Msec' | 'Ms';
declare type UnitAnyCase = Unit | Uppercase<Unit> | Lowercase<Unit>;
export declare type StringValue = `${number}` | `${number}${UnitAnyCase}` | `${number} ${UnitAnyCase}`;
interface Options {
    /**
     * Set to `true` to use verbose formatting. Defaults to `false`.
     */
    long?: boolean;
}
/**
 * Parse or format the given `val`.
 *
 * @param value - The string or number to convert
 * @param options - Options for the conversion
 * @throws Error if `value` is not a non-empty string or a number
 */
declare function ms(value: StringValue, options?: Options): number;
declare function ms(value: number, options?: Options): string;
export default ms;
```
`string`であればなんでも渡せたところから`StringValue`という型に絞られました。

まずは定義された他の型について見ていきます。`UnitAnyCase`はスケールを表す文字列をユニオンで列挙した`Unit`を用いて以下のような型を提供します。
```ts
type UnitAnyCase = Unit | Uppercase<Unit> | Lowercase<Unit>;
```
つまり、`Unit`で列挙した文字列に加えてそれらを全て大文字にしたケース、小文字にしたケースを含めてユニオンとして持ちます。

`StringValue`はそれらを用いて以下の定義されます。
```ts
type StringValue = `${number}` | `${number}${UnitAnyCase}` | `${number} ${UnitAnyCase}`;
```
`number`型を文字列にしたものか、それと`UnitAnyCase`を結合したもの、その間にスペースをひとつ設けたものしか許可されません。
このように`ms`は型を強く定義することで結果が`undefined`となることを事前に防げるような試みを行なっています([2020年のPR](https://github.com/vercel/ms/issues/184)から3.0.0のリリースについて議論されているのでまだしばらく使えないかもしれません)。