あるエンジニアがjavascriptを使用して開発を行っていたが、開発速度をあげるためにtypescriptを導入した。
型を定義することで変数の中身が明確になったりと、開発速度があがった。
しかし、型の定義を行っているはずが、型のバグに悩まされていた。
結論からいうと、tsconfigは厳しめに設定すべきだった
型を定義しても、それにそっていない内容のものがあり、想定外のところでバグを起こしてしまっていた。
以下のような例があげられる。
と、その前にクラスについて
クラスのインスタンス
インスタンス化、つまり、newしたものがインスタンス
newしていないものはインスタンスでない。インスタンスでないと、クラスメソッドなどを使用できない。
以下はただの object
を型アサーションで User
にしたもの。
インスタンスではないので、クラスメソッドの getFullName
を使用できない。(TypeErrorになる。)
class User {
public id: string;
public firstName: string;
public lastName: string;
constructor (input?: Partial<User>) {
this.id = input?.id ?? "id"
this.firstName = input?.firstName ?? "firstName"
this.lastName = input?.lastName ?? "lastName"
}
getFullName(): string {
return `${this.firstName} ${this.lastName}`
}
}
const _user = {
id: "id",
firstName: "firstName",
lastName: "lastName"
}
{
const user = _user as User;
console.log(_user instanceof User) // false
console.log(user instanceof User) // false
console.log(user.getFullName()) // TypeError: user.getFullName is not a function
}
{
const user = new User(_user);
console.log(user instanceof User) // true
console.log(user.getFullName()) // firstName lastName
}
constructorを省略
constructorを省略することで以下のような型と違う状況が起きる可能性がある。
class User {
id: string;
name: string;
}
const _user = new User();
_user.id = "id";
console.log(_user.name) // undefined
const user = new User();
user.id = "id";
user.name = "name";
console.log(user.name) // name
ただの型なのか、classを用意すべきかは考えたほうがいい。
もし型だけなら以下のようにする。
type User = {
id: string;
name: string;
}
const _user: User = {
id: "id"
};
// Property 'name' is missing in type '{ id: string; }' but required in type 'User'.
// 'name' is declared here.
const user: User = {
id: "id",
name: "name"
};
console.log(user) // { id: 'string', name: 'string' }
クラスを用意するならconstructorを定義する
class User {
id: string;
name: string;
constructor (input?: Partial<User>) {
this.id = input?.id ?? "id"
this.name = input?.name ?? "name"
}
}
const user = new User()
console.log(user.name) // name
delete(オブジェクトからプロパティを削除)
deleteにも要注意。型アサーションと同様のことが起きる。
class User {
id: string;
name: string;
constructor (input?: Partial<User>) {
this.id = input?.id ?? "id"
this.name = input?.name ?? "name"
}
}
const user = new User()
delete user.name
console.log(user.name) // undefined
他にもany型など開発者を泣かせるtypescriptの仕様がある。
以下のような記事にもあるとおり、 as
や any
はtypescriptの効果をなくしてしまう。
Docs
typescript: https://www.typescriptlang.org/
やるからには厳しくせなあかんな
(やるからには厳しくしないといけないな)