TypeORM便利ですね。
TypeORMはまだ未成熟ですが、それでもTypeORMが便利である最大の理由は、TypeScriptによる強力な静的解析の恩恵を受けられることです。
これにより、Java等の他の静的型言語で実装した場合はユニットテストか実行時エラーでしか検出できなかったエラーを実行時に検出できるようになります。
今回扱うのはいかにもJavaっぽいアノテーションもりもりのエンティティです。ここをうまく扱えなければTypeORMのメリットが半減してしまいます。
案1: よく見かける例
@Entity()
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@CreateDateColumn()
createdAt: Date;
}
サンプルでもっともよく見かける例です。シンプルで分かりやすいのですが、様々な問題点があります。
saveが失敗する
上記の例ではname
は必須フィールドですが、
const user = new User();
UserRepository.save(user);
のようなコードがコンパイルエラーなしで通ってしまい、
strictPropertyInitializationが使えない
TypeScriptのstrictPropertyInitialization
は、undefined
でないクラスのフィールドがコンストラクタで初期化されることを保障するための設定です。正直言って神機能です。
class Person {
name: string;
age: number; // 後から追記した
constructor(name: string) {
this.name = name;
}
}
上記の例では、コンストラクタでageフィールドが初期化されていないため、
new Person('Taro').age.toString();
などが実行時エラーとなってしまいます。これは危ない。しかしながら、strictPropertyInitialization
さえ有効にしていれば、このようなバグを未然に防ぐことができます。
案2: コンストラクタで初期化しちまえ!
こちらの方が安全性が高いでしょうか。
@Entity()
export class User {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column()
name: string;
@CreateDateColumn()
createdAt!: Date;
constructor(name: string) {
this.name = name;
}
}
この手法の問題点は、コンストラクタの仕様変更が怖くてできないことです。例えば、
constructor(firstName: string, lastName: string);
が、
constructor(lastName: string, firstName: string);
にいつの間にか入れ替わっていたとしたら・・・?恐ろしいバグが発生してしまいます。また、フィールド数が増えたときにコンストラクタが長いとソースコードが読みにくくなってしまうのも辛いところですね。
現実的な解: AutoGeneratedなカラムをOmitしたオブジェクトをObject.assignする
賛否両論あると思いますが今のところこれで落ち着きました。
@Entity()
export class User {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column()
name!: string;
@CreateDateColumn()
createdAt!: Date;
constructor(properties: UserInitializationProperties) {
Object.assign(this, properties);
}
}
export type UserInitializationProperties = Omit<User, 'id' | 'createdAt'>;
この手法のメリットとしては、
- 新しいフィールドを追加したとき、何もしなければエラーとなる
- 追加したフィールドはOmitされないので、コンストラクタの引数に渡すオブジェクトの必須フィールドとなります。
-
Rename Symbol
でフィールド名を簡単に変更できる- 例えば
name
フィールドをnamae
に変更しようと思ったとしても、VSCodeならF2キーを押すだけで簡単に変更できます。幸せ。
- 例えば
「こうしたほうがいいよ!」等ありましたら是非教えていただければと思いますm(_ _)m