はじめてOSSに新機能追加した
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のバグを潰したり新機能開発ができるという肌感がある。