この記事は、Business Bank Group Developers Advent Calendar 2日目の記事です。
はじめに
RxJSの5系統を使用して開発していた際に、Observableの型推測が正しく働かなかった事象を体験したので紹介しようと思います。
発生事象
const observable = Observable.of(new Task())
.do({
next: _.noop,
error: console.log
});
// エラーにならない。
observable.subscribe((val: number) => {
console.log(`result: ${val}`);
});
上記のコードはstreamを生成する際にエラー時に共通でログに出力などそういった処理をさせたい意図で書かれたコードになっています。
上記のコードはコメントにあるように、subscribe時の引数がnumberでもエラーになりません。
observableは定義された時、 observable: Observable<any>
になっているためです。
なぜ、anyになってしまうかというと、 _.noop
はlodashの関数になっていますが、これの定義に原因がありました。
noop(...args: any[]): void;
noopの定義ファイルでは上記のように定義されていたため、
observableはanyの引数を持つ、すなわちanyのstreamであるとvscodeに解釈されてsubscribe処理で自由な型指定ができてしまう現象になっていました。
回避策
いくつかあると思いますが、簡単なのは3つほど
const observer = Observable.of(new Model());
observer.do({
next: _.noop,
error: console.log
});
const observer: Observable<Model> = Observable.of(new Model())
.do({
next: _.noop,
error: console.log
});
const observer = Observable.of(new Model())
.do({
next: () => {},
error: console.log
});
その1は型推測に影響を与えるdoを分離させる方針。
その2は変数に型を宣言する。
その3は型推測に影響を与えるnextの関数の引数を正す。
のいずれかの対応で回避することが可能です。
感想とRxJS 6の話
そもそも後続に影響を与えないdo関数で型推論に影響を与えているのは腑に落ちない気持ちはありますが、
これはvscodeの限界だと認めて安心できるコードを保つために置き換えていくのが大事と感じます。
また、doはRxJS 6になると廃止になりpipeで行うようになるので、型推論の問題から解放されるかと思いましたが、
const observer = of(new Model)
.pipe<Model>(
tap((val: string) => console.log(val)),
map((val: number) => val.toString())
);
上記のようなコードでもエラーにならないで通ってしまうので、RxJSでの型はなかなか難しい部分があるのかもしれません。
明日のカレンダーは @kyokoshimizu さんが担当します。よろしくお願いします。