疑問
オプショナル型のhoge?: string
と、ユニオン型hoge: string | undefined
があったとき、両者の挙動に違いがあるのかないのかを検証したくなりました。
なおstrictNullCheck
とstrictPropertyInitialization
が有効な状況を前提とします。
検証
ブラウザ(Chrome)上で、次のコードを使って検証しました:TypeScirpt 公式 Playground
(TypeScriptのバージョンや細かいオプションなどはリンク先の設定を参照)
先に検証結果
- オブジェクトで実装した場合
- オプショナル型
hoge?: string
ではhoge
プロパティを省略できる - ユニオン型
hoge: string | undefined
のときはhoge
はプロパティを省略できない
- オプショナル型
- クラスで実装した場合
- どちらの型定義でも
hoge
プロパティへの代入を省略できる
- どちらの型定義でも
-
hoge?: string
とhoge: string | undefined
は違う型として評価される
オブジェクトの場合の挙動
まず2つのインタフェースを宣言します。
interface MyInterface1 {
hoge?: string;
}
interface MyInterface2 {
hoge: string | undefined;
}
MyInterface1の実装ではオプショナルなプロパティを省略できますが、
MyInterface2の実装ではプロパティは省略できません。
const obj1: MyInterface1 = {
// オプショナルなので省略できる
}
const obj2: MyInterface2 = {
// オプショナルでないプロパティは省略できない。省略するとコンパイルエラー
hoge: undefined
}
結果どちらの実装でもhogeプロパティはundefined
にできますが、
hasOwnProperty
をしたときには違いが出ます。
console.log(obj1.hasOwnProperty('hoge')); // false
console.log(obj2.hasOwnProperty('hoge')); // true
MyInterface2はMyInterface1を包含する型として評価されます。
そのため、MyInterface1の型にMyInterface2の型を代入することは可能です。
// これは可
const obj3: MyInterface1 = obj2;
その反対はできません。
// これはコンパイルエラー
const obj4: MyInterface2 = obj1;
クラスの場合の挙動
インタフェースの例と同じ型定義で2種類のクラスを宣言します。
このとき、コンストラクタ内でプロパティへの代入がなくてもエラーは出ません。
class MyClass1 {
public hoge?: string;
constructor() {
// オプショナルなので省略できる
}
}
class MyClass2 {
public hoge: string | undefined;
constructor() {
// 代入しなくてもコンパイルが通る!
}
}
どちらのクラスのインスタンスでもhogeについてのhasOwnProperty
の結果はfalse
になります。
const myClass1 = new MyClass1();
const myClass2 = new MyClass2();
console.log(myClass1.hasOwnProperty('hoge')); // false
console.log(myClass2.hasOwnProperty('hoge')); // false
MyClass2はMyClass1の型を包含するのでMyClass1の型にMyClass2の型を代入することは可能です。
その逆の代入はできません。
// これは可
const myClass3: MyClass1 = new MyClass2();
// これはコンパイルエラー
const myClass4: MyClass2 = new MyClass1();
インタフェースの型とクラスの型の互換性
同じ型定義なので、当然相互の代入が可能です。
const myObj1: MyInterface1 = myClass1;
const myObj2: MyInterface2 = myClass2;
const myObj3: MyClass1 = obj1;
const myObj4: MyClass2 = obj2;
まとめ
確認した挙動をまとめるとこのようになります。
- オプショナル型の
hoge?: string
と、ユニオン型hoge: string | undefined
は同じではない - オブジェクトでの実装時には、プロパティを省略できるかできないかが違う
- クラスでの実装時には、どちらの型定義でもプロパティへの代入を省略できてしまう