TypeScript Deep Dive 日本語版
  • TypeScript Deep Dive 日本語版
  • TypeScript入門 & 環境構築
    • なぜTypeScriptを使うのか?
  • JavaScript
    • 等価演算子の同一性
    • リファレンス
    • nullとundefined
    • this
    • クロージャ
    • Number型
    • Truthy
  • モダンなJavaScriptの機能
    • クラス
      • Classes Emit
    • アロー関数
    • 残余引数(Restパラメータ)
    • let
    • const
    • 分割代入
    • スプレッド演算子
    • for...of
    • Iterator
    • テンプレートリテラル
    • Promise
    • ジェネレータ
    • async await
  • プロジェクトの環境設定
    • コンパイルコンテキスト
      • tsconfig.json
      • コンパイル対象ファイルの設定
    • 宣言空間
    • ファイルモジュール
      • ファイルモジュールの詳細
      • global.d.ts
    • 名前空間
    • 動的インポート
  • Node.js & TypeScriptのプロジェクト作成
  • React & TypeScriptのプロジェクト作成
  • TypeScriptの型システム
    • JavaScriptからの移行ガイド
    • @types パッケージ (DefinitelyTyped)
    • アンビエント宣言(declare)
      • 型定義ファイル
      • グローバル変数の宣言
    • インターフェース
    • Enum
    • lib.d.ts
    • 関数の型
    • 呼び出し可能オブジェクト
    • Type Assertion(型アサーション)
    • Freshness
    • 型ガード
    • リテラル型
    • Readonly
    • ジェネリック型
    • 型推論
    • 型の互換性
    • never
    • 判別可能なUnion型
    • Index signature(インデックス型)
    • 型の移動
    • 例外のハンドリング
    • ミックスイン
  • JSX
    • React
    • React以外のJSX
  • オプション
    • noImplicitAny
    • strictNullChecks
  • TypeScriptのエラー
    • エラーの理解
    • 一般的なエラー
  • NPM
  • テスト
    • Jest
    • Cypress
  • ツール
    • Prettier
    • Husky
    • Changelog
  • その他のヒント
    • String Based Enums
    • Nominal Typing
    • Stateful Functions
    • Bind is Bad
    • Currying
    • Type Instantiation
    • Lazy Object Literal Initialization
    • Classes are Useful
    • Avoid Export Default
    • Limit Property Setters
    • outFile caution
    • JQuery tips
    • static constructors
    • singleton pattern
    • Function parameters
    • Build Toggles
    • Barrel
    • Create Arrays
    • Typesafe Event Emitter
  • スタイルガイド(コーディング規約)
  • TypeScriptコンパイラの内側
    • Program
    • AST
      • TIP: Visit Children
      • TIP: SyntaxKind enum
      • Trivia
    • Scanner
    • Parser
      • Parser Functions
    • Binder
      • Binder Functions
      • Binder Declarations
      • Binder Container
      • Binder SymbolTable
      • Binder Error Reporting
    • Checker
      • Checker Diagnostics
      • Checker Error Reporting
    • Emitter
      • Emitter Functions
      • Emitter SourceMaps
    • Contributing
GitBook提供
このページ内
  • どちらであるかをチェックする
  • ルートレベル(root level)のundefinedのチェック
  • undefined の明示的な利用を制限する
  • Nodeスタイルのコールバック
  • 値の有効性を表す意味でundefinedを使用しない
  • JSONとシリアライズ
  • 結論

役に立ちましたか?

  1. JavaScript

nullとundefined

JavaScript(と、TypeScript)は、nullとundefinedという2つのボトム型(bottom type)があります。これらは異なる意味を持っています。

  • 初期化されていない: undefined。

  • 現在利用できない: null。

どちらであるかをチェックする

現実としては、あなたは両方とも対応する必要があります。==でチェックしましょう:

/// `foo.bar == undefined` のようなコードを書いたときに、何が起きるか想像してみてください:
console.log(undefined == undefined); // true
console.log(null == undefined); // true

// このようなチェックをすれば、falsyな値について心配する必要はありません
console.log(0 == undefined); // false
console.log('' == undefined); // false
console.log(false == undefined); // false

== nullを使ってundefinedと nullを両方ともチェックすることを推奨します。一般的に2つを区別する必要はありません。

function foo(arg: string | null | undefined) {
  if (arg != null) {
    // `!=` がnullとundefinedを除外しているので、引数argは文字列です
  }
}

1つだけ例外があります。次に説明するルートレベル(root level)のundefinedの値です。

ルートレベル(root level)のundefinedのチェック

== nullを使うべきだと言ったことを思い出してください。もちろん、覚えているでしょう(今、言ったばかりなので)。それは、ルートレベルのものには使用しないでください。 strictモード(strict mode)でfooを使うとき、fooが定義されていないと、ReferenceError exceptionが発生し、コールスタック全体がアンワインド(unwind)されます。

strictモードを使うべきです...現実としては、TSコンパイラはモジュール(modules)を使うときに、自動的に"use strict";を挿入します...後で解説を行うので、詳細は省略します:)

変数が_global_レベルで定義されているかどうかを確認するには、通常、typeofを使用します:

if (typeof someglobal !== 'undefined') {
  // これでsomeglobalは安全に利用できます
  console.log(someglobal);
}

undefined の明示的な利用を制限する

TypeScriptでは値と構造を分離してドキュメントのようにわかりやすくすることができます。下記のようなコードを想像してください:

function foo(){
  // if 何らかの場合に返す値
  return {a:1,b:2};
  // else それ以外の場合に返す値
  return {a:1,b:undefined};
}

これは、次のように型アノテーションを使用すべきです。

function foo():{a:number,b?:number}{
  // if 何らかの場合に返す値
  return {a:1,b:2};
  // else それ以外の場合に返す値
  return {a:1};
}

Nodeスタイルのコールバック

Nodeスタイルのコールバック関数(例: (err, somethingElse)=> {/* something */})は、エラーがなければ errにnullを設定して呼び出されます。開発者は一般的にtruthyチェックを行います:

fs.readFile('someFile', 'utf8', (err,data) => {
  if (err) {
    // 何らかのエラー処理をする
  } else {
    // エラーなし
  }
});

独自のAPIを作成するときは、一貫性のためにnullを使用することは、良くはありませんが、問題ありません。とはいえ、できればPromiseを返すようにするべきです。そうすれば、errがnullかどうかを気にかける必要はなくなります(.thenと.catchを使います)。

値の有効性を表す意味でundefinedを使用しない

下記は、ひどい関数の例です:

function toInt(str:string) {
  return str ? parseInt(str) : undefined;
}

下記のようにする方が、はるかに良いでしょう:

function toInt(str: string): { valid: boolean, int?: number } {
  const int = parseInt(str);
  if (isNaN(int)) {
    return { valid: false };
  }
  else {
    return { valid: true, int };
  }
}

JSONとシリアライズ

JSON標準では、nullのエンコードはサポートしていますが、undefinedのエンコードはサポートしていません。値がnullである属性を持つオブジェクトをJSONにエンコードするとき、その属性はnull値とともにJSONに含まれますが、値がundefinedである属性は完全に除外されます。

JSON.stringify({willStay: null, willBeGone: undefined}); // {"willStay":null}

結果として、JSONベースのデータベースは、nullはサポートしますがundefinedはサポートしないことがあります。null値を持つ属性はエンコードされるため、オブジェクトをエンコードしてリモートのデータストアに送る前に、ある属性値をnullにすることで、その属性をクリアしたいという意図を明確に伝えることができます。

属性値をundefinedにすると、その属性はJSONにエンコードされないため、データのストレージと転送のコストを節約することができます。しかし、これによって、値をクリアすることと、値が存在しないことの文脈を曖昧にしてしまいます。

結論

しかし、Nodeスタイルのコードでは、Error引数にnullが標準で使われています。これは何かがおかしいですという意味です。私は個人的に、ほとんどのプロジェクトにおいて、意見がバラバラのライブラリを使っていますが、== nullで除外するだけなので、2つの区別について気にすることはありません。

前へリファレンス次へthis

最終更新 2 年前

役に立ちましたか?

TypeScriptチームは、nullを使いません: 。 そして、問題は起きていません。 Douglas Crockfordはであり、誰もがundefinedだけを使うべきだと考えています。

TypeScriptコーディングガイドライン
nullは良くない考え