TypeScriptでPromise<T>な戻り値でnullが返ってくる問題について

August 26, 2020,
tags: typescript


このエントリーをはてなブックマークに追加

最近TypeScriptでサーバサイドの実装をしている。
特に問題なく快適にかけているが、やっぱりハマるところがある。

それは「Promiseな戻り値でnullが返ってくる」という問題。

何が起きたのか?

Fireormを使ってfirebaseのCloud Firestoreからデータを取得しようと以下のようなコードを書いた。


import { Collection, getRepository } from 'fireorm'

@Collection('user')
export class User {
  id: string
  name: string
}

const userRepository = getRepository(User)
const token = await userRepository.findById("user_1")

このときに疑問に思ったのはfindById で存在しないIDを指定したらどのような挙動をするのか?
自分は「Promiseで戻り値が保証されているのでErrorがthrowされる」のかなと思った。しかし違った。
返ってきたのはそう「null」だった。

Javaの場合は「戻り値型を指定してのnullを返すこと」ができる。

import java.io.*;

class HogeRun {
	public static void main (String[] args) {
	    HogeRepository HogeRepository = new HogeRepository();
		System.out.println(HogeRepository.findById("aaaa"));
	}
}


class Hoge {}

class HogeRepository {
    public Hoge findById(String $id) {
        return  null;
    }
}

しかし同じようなコードをTypeScriptで書くと普通はコンパイルエラーになる。

class Hoge {
    readonly id: string|undefined
    readonly name: string|undefined
}

class HogeRepository {
    findById(id: string) : Hoge {
        return null
    }
}

playground

Fireormのrepository.findByIdから戻された値はnullだったのだ…。

Fireormの実装について

AbstractFirestoreRepository

FireormのAbstractFirestoreRepositoryの実装箇所を見ると明らかだが、doc.existsじゃない場合にreturn null を実装している箇所がある。
これはなぜコンパイルエラーにならないのか?

strictNullChecksオプションについて

https://typescript-jp.gitbook.io/deep-dive/intro/strictnullchecks

strictNullChecksをtrueにすると、コンパイル時にコンパイル時にnullエラーが発生するが、そうじゃないとランタイム時にエラーが発生する。
なので return null なコードも実行時にエラーになるというか、実行時はjsなわけでエラーにならないと…。

Fireormが return null していてもコンパイルエラーにならないのは strictNullCheckstrue ではないからということかと思います。
ためしにコードを落としてきて、tsconfigでstrictNullChecks: true にしてビルドするとエラーになる。

手元のプロジェクトで strictNullChecksを有効にしなくても returen nullがエラーになる

手元にあるプロジェクトで 戻り地の型指定しながらreturn null なコードを書いたらエラーになった。 しかしstrictNullChecks は指定していない。
なぜだろう?

tsconfig.jsにはstrict: trueと記載があるため、エラーになっていた。
strict: true を指定すると何が起きるのか?

https://www.typescriptlang.org/docs/handbook/compiler-options.html

Enabling —strict enables —noImplicitAny, —noImplicitThis, —alwaysStrict, —strictBindCallApply, —strictNullChecks, —strictFunctionTypes and —strictPropertyInitialization.

ということらしい。
つまりstrictNullChecks は有効になっている。なので、エラーになるということ。

まとめ

  • 戻り値の型が指定されていていも、tsconfigの状況によってはnullを返す
  • TypeScriptな他のライブラリを使うときはstrict or strictNullChecks のオプションがどうなっているかを見てる

最後に

strictNullChecksを教えていただいた@_sunnyoneさんありがとうございました。
strictについて教えていただいた@nanopx大先生ありがとうございました。

comments powered by Disqus