Ryutaro Takemura

【TypeScript】メソッド内で使用しているthisをTypeScriptに認識させてエラーを出力してもらいたい

9 min read

仕様

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を防ぐことができました。


Ryutaro Takemura

Hello