【TypeScript】メソッド内で使用しているthisをTypeScriptに認識させてエラーを出力してもらいたい
仕様
TS/JS のthis
は定義時に参照するオブジェクトが決まるのではなく、実行された場所によって決まる。
そしてTypeScriptはメソッドの中でthis
が使われていることを通常認識しないので思わぬ結果が表示されてしてしまう場面があります。
例としてPersonクラスを定義してその中で自己紹介をするgreeting
メソッドを定義したとします。
例)
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
greeting() {
console.log(`こんにちは、私の名前は${ this.name }です。`);
}
}
const yamada = new Person('山田');
yamada.greeting();
- 実行
$ tsc index.ts
$ node index.js
こんにちは、私の名前は山田です。
期待通りの内容が出力されます。
ここでPerson
クラス内のname
プロパティをコメントアウトすると、当たり前ですがname
プロパティが無いぞ!とエラーが発生します。
class Person {
// コメントアウトする
// name: string;
constructor(name: string) {
this.name = name;
}
greeting() {
// thisが参照しているPersonクラスにnameプロパティが無いためエラーが発生する
console.log(`こんにちは、私の名前は${ this.name }です。`);
}
}
const yamada = new Person('山田');
yamada.greeting();
- 実行
$ tsc index.ts
error TS2339: Property 'name' does not exist on type 'Person'.
ここでエラーに気づいてname
プロパティを追加することができます。
クラス外のオブジェクトでメソッドを呼び出す時もエラーを発生させたい
this
は定義時ではなく実行時に参照するオブジェクトが変化するため別のオブジェクト内でもname
プロパティがない場合、同じようにname
プロパティが無いよ!と怒って欲しいがエラーを出力してくれません。
確認のため新たに別のオブジェクトを作成してPersonクラス内のgreeting()
メソッドを呼び出してみます。
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
greeting() {
console.log(`こんにちは、私の名前は${ this.name }です。`);
}
}
const yamada = new Person('山田');
// 別のオブジェクトを作成する
const fullName = {
fullNameGreeting: yamada.greeting
}
fullName.fullNameGreeting();
- 実行
$ tsc index.ts
$ node index.js
こんにちは、私の名前はundefinedです。
この場合、エラーが発生せず実行結果を見てみるとundefined
となっています。
なぜPerson内にname
プロパティが無い時はエラーとなってくれたのに、作成したfullName
内にname
プロパティがなくてもエラーとならずundefined
となってしまうのか。
TypeScriptはメソッドの中でthis
が使われていることを知らない(認識できていない)エラーとなってくれません
そこで今回はTypeScriptにgreeting
メソッドの中でthis
が使われていることを明示的に認識させてあげます。
方法は次のとおりです。
greeting
メソッドの第1引数にthis: { name: string }
を入れてコンパイルしてみます。
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
// 第一引数に this を入れて型を指定します
greeting(this: { name: string }) {
console.log(`こんにちは、わたしの名前は${ this.name }です`);
}
}
const yamada = new Person('山田');
yamada.greeting();
const fullName = {
fullNameGreeting: yamada.greeting
}
fullName.fullNameGreeting();
- 実行
$ tsc index.ts
error TS2684: The 'this' context of type '{ fullNameGreeting: (this: { name: string; }) => void; }' is not assignable to method's 'this' of type '{ name: string; }'.
Property 'name' is missing in type '{ fullNameGreeting: (this: { name: string; }) => void; }' but required in type '{ name: string; }'.
エラーとなってくれました!これで「あっ!nameプロパティ入れ忘れた」または「呼び出し側のnameプロパティを探しちゃってるじゃん」となれるわけです。
確認のためname
プロパティを追加して実行がしてみます
class Person {
.
.
.
// 第一引数に this を入れて型を指定します
greeting(this: { name: string }) {
console.log(`こんにちは、わたしの名前は${ this.name }です`);
}
}
const yamada = new Person('山田');
const fullName = {
// エラーが出たのでnameプロパティを追加
name: "山田 太郎",
fullNameGreeting: yamada.greeting
}
fullName.fullNameGreeting();
- 実行
$ tsc index.ts
$ node index.js
こんにちは、わたしの名前は山田 太郎です
コンパイル時にエラーが表示されることで、未然にundefined
を防ぐことができました。