檜山正幸のキマイラ飼育記 このページをアンテナに追加 RSSフィード Twitter

キマイラ・サイトは http://www.chimaira.org/です。
トラックバック/コメントは日付を気にせずにどうぞ。
連絡は hiyama{at}chimaira{dot}org へ。
蒸し返し歓迎!
このブログの更新は、Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama
ところで、アーカイブってけっこう便利ですよ。

2018-01-12 (金)

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

| 18:43 | 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);
}

通りすがり通りすがり 2018/01/12 13:59 コンパイルの際に--strictNullChecksを付けたらどうでしょう?

Nfm4yxnW8Nfm4yxnW8 2018/01/12 18:52 シングルトン型ってString Literal Types,Numeric Literal Typesじゃダメなんですか?(strictオプションはonで)

https://www.typescriptlang.org/docs/handbook/advanced-types.html#enum-member-types

m-hiyamam-hiyama 2018/01/12 18:54 通りすがりさん、Nfm4yxnW8さん、ありがとうございます。
ごめんなさい、修正記事書きました。今日の2番めの記事です。