
owという、Lots of built-in validationsなOSSに、BigIntのバリデーションを追加した。
owに関しては2月頃に3回、型整備のPRを出してマージされていたので、コードには少し馴染みがあった。
https://wafuwafu13.hateblo.jp/#sindresorhusow
OSSの型整備より一歩踏み込んだことがしたいと思い、issueを消化することを目標に最近やっていた。
実際、null, undefinedに起因するバグがあったのでそれを消化できた。
そして今回、BigIntのサポートをするという、約3年前のissueがあったので拾ってやってみた。
Support BigInt validator · Issue #54 · sindresorhus/ow · GitHub
owの構造と、何をどう追加したのかを雑にメモしておく。
まず、README.mdにも載っている以下のコードでArgumentErrorがどのようにして起こるのかをみていく。
import ow from 'ow'; const unicorn = input => { ow(input, ow.string.minLength(5)); // … }; unicorn('yo'); //=> ArgumentError: Expected string `input` to have a minimum length of `5`, got `yo`
source/predicate/string.tsに、ow.stringに関するコードが定義されており、minLengthの定義は以下のようになっている。
ow/string.ts at 49841a260cd431985b91d319b091dd59096775a3 · sindresorhus/ow · GitHub
minLength(length: number): this { return this.addValidator({ message: (value, label) => `Expected ${label} to have a minimum length of \`${length}\`, got \`${value}\``, validator: value => value.length >= length, negatedMessage: (value, label) => `Expected ${label} to have a maximum length of \`${length - 1}\`, got \`${value}\`` }); }
このvalue => value.length >= lengthのバリデーターは、source/predicates/predicate.tsで発火される。
messageの発火も同じ部分である。
ow/predicate.ts at 49841a260cd431985b91d319b091dd59096775a3 · sindresorhus/ow · GitHub
ow/predicate.ts at 49841a260cd431985b91d319b091dd59096775a3 · sindresorhus/ow · GitHub
...
result = validator(value);
...
const errorMessage = message(value, label_, result);
このvalidatorの実行結果がtrueでなければ、最終的に独自定義のArgumentErrorが投げられる。
ow/predicate.ts at 49841a260cd431985b91d319b091dd59096775a3 · sindresorhus/ow · GitHub
throw new ArgumentError(message, main, errors);
ArgumentErrorが投げられるのはこれ以外にも、ow(input, ow.type.foo)においてinputとtypeの型が合っていない場合がある。
例えばtest/string.tsでテストされているような場合がある。
ow/string.ts at 49841a260cd431985b91d319b091dd59096775a3 · sindresorhus/ow · GitHub
t.throws(() => { ow(12 as any, ow.string); }, 'Expected argument to be of type `string` but received type `number`');
このバリデーションは、Predicateクラスのコンストラクタで定義されている。
ow/predicate.ts at 49841a260cd431985b91d319b091dd59096775a3 · sindresorhus/ow · GitHub
this.addValidator({ message: (value, label) => { // We do not include type in this label as we do for other messages, because it would be redundant. const label_ = label?.slice(this.type.length + 1); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing return `Expected ${label_ || 'argument'} to be of type \`${this.type}\` but received type \`${is(value)}\``; }, validator: value => (is as any)[typeString](value) });
ここのバリデーションで使われているisは、owと同じ作者が作ったライブラリである。
github.com
今回やったissueに立ち返ってみると、isでBigInt detectionがサポートされたので、それをowにも取り込もう、というものだった。
Support BigInt detection · Issue #39 · sindresorhus/is · GitHub
最初は、source/predicates/number.tsにbigintメソッドを足して、ow(BigInt(9007199254740991), ow.number.bigint)で判定させようと思ったが、numberとbigintで型が違うため、コンストラクタのバリデーションで意図せず弾かれてしまった。
新たにBigIntPredicateクラスを定義し、意図してコンストラクタのバリデーションで弾かれる方針で実装を進めた。
NodeのBigIntのサポートが10.4.0からなのでテストが思うように動かなかったりいろいろしたが、無事マージされた。
このOSSの作者はFull-Time Open-Sourcererでレスポンスもレビューも手早くて時差があるものの開発体験がとても良かった。
ソースコードの核となる部分の7割くらいを理解できれば、issueのバグを潰したり新機能開発ができるという肌感がある。