このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

TypeScript、僕が悪かった、ゴメン: nullやundefinedの扱いはマトモだった

ごめんなさい。↑の記事、僕がうかつな事を書きました。TypeScriptのコンパイラにオプションを付ければ、nullとundefinedに関してキチンとチェックします。

きびしいチェックをするオプション付きなら、null型もundefined型もシングルトン型になります。

デフォルトでの値の集合 きびしくチェックしたときの値の集合
null {null, undefined} {null}
undefined {null, undefined} {undefined}
void {null, undefined} {undefined}

事の発端と顛末

TypeScriptの列挙型の挙動を知りたくて、enum OnlyOne {ONE} のサンプルを書いて試したら、nullが入っていて驚いた、というのが事の発端です。

このとき、単なるディレクトリ(フォルダ)に enum-test.ts を置いて、tsc enum-test.ts とやってみた、という状況。その後も、同じ“素のディレクトリ”で試していたので、コンパイラ(トランスパイラ)の挙動はオプションなしのデフォルトだったんです。

冷静に考えてみると、例えばstringにnullが入り込むならもう少し早い時点でその事実に気づいていてもよさそう。実は、以前は“素のディレクトリ”ではなくて“プロジェクト・ディレクトリ”で作業していたのでした。

プロジェクト・ディレクトリには、次のようなtsconfig.jsonが置いてありました。(存在を意識してなかったけど。)

{
  "compilerOptions": {
    "strictNullChecks": true,
    "noUnusedLocals" : true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "lib": ["dom", "ES2017"],
    "module": "ES2015",
    "target": "ES5",
  }
}

コンパイラ・オプションはきびしいチェックになっていたので、僕の期待通りに動いていたのですよね。それゆえに、不審な事態に遭遇しなかったわけです。

実は、環境が変わったので態度が変わっただけなのを誤解して、「君って、そういうコだったんだ」「信じていたのに、今まで騙されていたんだな、俺は!」と短絡的に反応してしまいました。

すべてチェックします

下のサンプルは、コンパイラ・オプションなしだとノーエラーでコンパイルされ、きびしいオプションを付けると全部コンパイルエラーになる例です。

// void型の要素は何か?
// >>> コンパイルエラー

function NullInVoid() : void {
  return null;
}

// null型の要素は何か?
// >>> コンパイルエラー

function UndefInNull() : null {
  return undefined;
}

// undefined型の要素は何か?
// >>> コンパイルエラー

function NullInUndef() : undefined {
  return null;
}

// nullが基本型(プリミティブ型)に入るか?
// >>> コンパイルエラー

function NullInNumber() : number {
  return null;
}

function NullInBoolean() : boolean {
  return null;
}

function NullInString() : string {
  return null;
}

// undefinedが基本型(プリミティブ型)に入るか?
// >>> コンパイルエラー

function UndefInNumber() : number {
  return undefined;
}

function UndefInBoolean() : boolean {
  return undefined;
}

function UndefInString() : string {
  return undefined;
}

// 値がひつとの列挙型
enum OnlyOne {
  ONE
}

// nullがOnlyOneに入るか?
// >>> コンパイルエラー
function NullInOnlyOne() : OnlyOne {
  return null;
}

// undefinedがOnlyOneに入るか?
// >>> コンパイルエラー
function UndefInOnlyOne() : OnlyOne {
  return undefined;
}

// 文字列の繰り返し連結
function repeatString(s:string, n:number) :string {
  var r:string = "";
  for (var i = 0; i < n; i++) {
    r = r + s;
  }
  return r;
}

// 不適切な呼び出しの例
// >>> コンパイルエラー
function doRepeatString() {
  return repeatString(null, undefined);
}