Classの初期化を出来るだけ楽にする
読むのがめんどくさい人はこちら
Classの初期化って面倒くさい
class User {
public readonly id: number;
public readonly name: string;
public readonly age: number;
public readonly email?: string;
constructor(
id: number,
name: string,
age: number,
email?: string,
) {
this.id = id;
this.name = name;
this.age = age;
this.email = email;
}
}
Classに必要な初期化処理を愚直に毎回書くとめちゃくちゃめんどくさい。
上記のような処理を毎回書くのは骨が折れる。
ちょっと楽にする
class User {
constructor(
public readonly id: number,
public readonly name: string,
public readonly age: number,
public readonly email?: string,
) {}
}
実はコンストラクタにフィールドを定義する事が出来る。
これだけでも記述量が半分になって楽になる。
まだ面倒な事が・・・
クラスの記述は楽になったけど、生成処理はどうだろうか。
const user = new User(1, '田中', 18, 'tanaka@example.com');
引数の順番覚えるのめんどくさい・・・
何番目がどのプロパティになるんだっけ・・・?
これプロパティ増えてきたらやばくない?
もっと楽にする
type AnyFunction = (...args: any[]) => any;
type KeysOfType<T, S> = {
[key in keyof T]: S extends T[key] ? key : never;
}[keyof T];
type UndefinedToOptional<T> =
Omit<T, KeysOfType<T, undefined>> &
Partial<Pick<T, KeysOfType<T, undefined>>>;
type Fields<T> = UndefinedToOptional<Omit<T, KeysOfType<T, AnyFunction>>>;
TypeScriptの型パズルパワーを借りて楽にしよう。
クラス定義は下記のように変更する。
class User {
public readonly id!: number;
public readonly name!: string;
public readonly age!: number;
public readonly email?: string;
constructor(props: Fields<User>) {
Object.assign(this, props)
}
}
ちょっとだけ記述量は増えたかな。
生成処理はどうだろう?
const user = new User({
id: 1,
name: '田中',
age: 18,
email: 'tanaka@example.com',
});
プロパティを指定するようになったから可読性が高くなった気がするね。
これなら今後、プロパティが増えても大丈夫かな。
型定義解説
AnyFunction
type AnyFunction = (...args: any[]) => any;
何らかの関数を表現する型定義。
const func1: AnyFunction = () => {};
const func2: AnyFunction = (id: number) => {};
const func3: AnyFunction = () => 1;
上記のように関数であれば適合する事が出来る。
KeysOfType
type KeysOfType = {
[key in keyof T]: S extends T[key] ? key : never;
}[keyof T];
Objectから該当する型のキーを取得する型定義。
ConditionalTypesを使用して、指定された型に適合するキー名だけを抽出している。
type Keys = KeysOfType<{
id: number;
name: string;
email: string;
}, string>;
const key1: Keys = 'name';
const key2: Keys = 'email';
上記の場合Keys型はname
とemail
のTuple型になる。
UndefinedToOptional
type UndefinedToOptional<T> =
Omit<T, KeysOfType<T, undefined>> &
Partial<Pick<T, KeysOfType<T, undefined>>>;
undefinedなプロパティをOptionalに変更する型定義。
Objectからundefinedを含まないプロパティを抽出した物と、undefinedを含むプロパティを抽出しPartialでラップした物を合成する事でundefinedをOptionalに変更している。
type User = {
id: number;
name: string;
email: string | undefined;
};
const user1: User1 = {
id: 1,
name: '田中',
emai: undefined,
};
const user2: UndefinedToOptional<User2> = {
id: 1,
name: '田中',
};
上記のようにUndefinedToOptionalでラップする事により、undefinedなプロパティを省略する事が出来る。
Fields
type Fields<T> = UndefinedToOptional<Omit<T, KeysOfType<T, AnyFunction>>>;
クラスから初期化に必要なフィールドを抽出する型定義。
クラスにはプロパティ以外にもメソッドが生えている場合があるので、KeysOfType
とAnyFunction
を利用して関数型を除くプロパティのみを取得するようにしている。
Discussion