Dartではtrue以外はfalseとして振る舞う

JavaScriptでは様々な値が真偽値として特徴的な振る舞いを見せます。  

### falseとして振る舞う値

- undefined
- null
- false
- -0, 0, NaN
- ""

### trueとして振る舞う値

- true
- -0, 0, NaN以外のnumber
- ""以外のstring
- その他のオブジェクト

これらはif文で活用することで読みやすくも読みにくくもなる面白いテクニックですが、同様のことをDartでも検証してみました。  

## undefined

Dartにはそもそもundefinedという値はありません。また、未定義のシンボルを呼びだそうとすると実行時エラーとなりますので、undefinedについては真偽値が得られません。

```dart
  /// NoSuchMethodError: method not found 'get:obj'
  test("undefined", () {
    if (obj) {
      expect(true, isFalse);
    } else {
      expect(false, isFalse);
   }
  });
```

面白いのは、`obj`という名前の変数がないとき、`obj`のgetterがないというエラーになることですね。

## null

nullに関してはJavaScriptと同様の振る舞いを見せました。これは直感的なので違和感もないですね。

```dart
  /// Passed : null => false
  test("null", () {
    var obj;
    if (obj) {
      expect(true, isFalse);
    } else {
      expect(false, isFalse);
    }
  });
```

## number

numberはすべての場合においてfalseとして振る舞いました。

```dart
  group("number", () {
    /// Passed : 0 => false
    test("0", () {
      if (0) {
        expect(true, isFalse);
      } else {
        expect(false, isFalse);
      }
    });
    /// Passed : -0 => false
    test("-0", () {
      if (-0) {
        expect(true, isFalse);
      } else {
        expect(false, isFalse);
      }
    });
    /// Failed : -1 => false
    test("-1", () {
      if (-1) {
        expect(true, isTrue);
      } else {
        expect(false, isTrue);
      }
    });
    /// Failed : 1 => false
    test("1", () {
      if (1) {
        expect(true, isTrue);
      } else {
        expect(false, isTrue);
      }
    });
    /// Passed : NaN => false
    test("NaN", () {
      if (double.NAN) {
        expect(true, isFalse);
      } else {
        expect(false, isFalse);
      }
    });
  });
```

## string

stringにおいても、すべての場合でfalseとして振る舞いました。

```dart
  group("string", () {
    /// Passed : "" => false
    test("empty", () {
      if ("") {
        expect(true, isFalse);
      } else {
        expect(false, isFalse);
      }
    });
    /// Failed : "true" => false
    test("true string", () {
      if ("true") {
        expect(true, isTrue);
      } else {
        expect(false, isTrue);
      }
    });
  });
```

## その他のオブジェクト

その他のオブジェクトに関してもすべてfalseの振る舞いになりました。

```dart
  group("object", () {
    /// Passed : {} => false
    test("empty object", () {
      if ({
      }) {
        expect(true, isFalse);
      } else {
        expect(false, isFalse);
      }
    });
    /// Failed : object => false
    test("not empty object", () {
      if ({
          "name" : "laco",
          "flag" : false
      }) {
        expect(true, isTrue);
      } else {
        expect(false, isTrue);
      }
    });
  });
```

# まとめ

### Dartでtrueとして振る舞う値

- true

### Dartでfalseとして振る舞う値

- その他すべて

ということになりました。さすが型安全というべきでしょう。感動しました。今回の調査に使ったソースコードを最後に添付しておきます。実行は`$ dart object_as_bool_test.dart`です。  

ちなみに、dartの実行時に`$ dart --checked object_as_bool_test.dart`とすると、boolean以外を真偽値として扱おうとしている事自体がエラーとなってテストがすべてエラー落ちします。`use struct`みたいなものですね。  

```dart:object_as_bool_test.dart
import 'package:unittest/unittest.dart';

main() {

  /// NoSuchMethodError: method not found 'get:obj'
  test("undefined", () {
    if (obj) {
      expect(true, isFalse);
    } else {
      expect(false, isFalse);
    }
  });
  /// Passed : null => false
  test("null", () {
    var obj;
    if (obj) {
      expect(true, isFalse);
    } else {
      expect(false, isFalse);
    }
  });
  group("number", () {
    /// Passed : 0 => false
    test("0", () {
      if (0) {
        expect(true, isFalse);
      } else {
        expect(false, isFalse);
      }
    });
    /// Passed : -0 => false
    test("-0", () {
      if (-0) {
        expect(true, isFalse);
      } else {
        expect(false, isFalse);
      }
    });
    /// Failed : -1 => false
    test("-1", () {
      if (-1) {
        expect(true, isTrue);
      } else {
        expect(false, isTrue);
      }
    });
    /// Failed : 1 => false
    test("1", () {
      if (1) {
        expect(true, isTrue);
      } else {
        expect(false, isTrue);
      }
    });
    /// Passed : NaN => false
    test("NaN", () {
      if (double.NAN) {
        expect(true, isFalse);
      } else {
        expect(false, isFalse);
      }
    });
  });
  group("string", () {
    /// Passed : "" => false
    test("empty", () {
      if ("") {
        expect(true, isFalse);
      } else {
        expect(false, isFalse);
      }
    });
    /// Failed : "true" => false
    test("true string", () {
      if ("true") {
        expect(true, isTrue);
      } else {
        expect(false, isTrue);
      }
    });
  });
  group("object", () {
    /// Passed : {} => false
    test("empty object", () {
      if ({
      }) {
        expect(true, isFalse);
      } else {
        expect(false, isFalse);
      }
    });
    /// Failed : object => false
    test("not empty object", () {
      if ({
          "name" : "laco",
          "flag" : false
      }) {
        expect(true, isTrue);
      } else {
        expect(false, isTrue);
      }
    });
  });
}
```