※このブログではサーバー運用、技術の検証等の費用のため広告をいれています。
記事が見づらいなどの問題がありましたらContactからお知らせください。


【TypeScript】グローバル関数のthisの型を指定する

TypeScript

投稿日:2020年12月25日

このエントリーをはてなブックマークに追加
TypeScript、JavaScriptではグローバルな関数のthisが暗黙的に割り当てられるために注意が必要です。TypeScriptの型指定の機能を使うことでこのthisの割り当てを危険性の低いものに変えることができます。

実践

thisの問題点

TypeScript(JavaScriptも同様)では関数のthisの割り当てにある特徴があります。

例えばまず次の様なユーティリティ関数を作成したとします。

function sendGreeting() {
    console.log(`Hello ${this.getFullName()}`);
}

この関数はgetFullName()という関数を持つObjectにバインドされることを前提にthisを利用して作られています。

call()apply()bind()メソッドを使って以下の様に使用できます。

class User {
    public readonly firstName: string;
    public readonly lastName: string;
    constructor(firstName: string, lastName: string) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName(): string {
        return `${this.firstName} ${this.lastName}`;
    }
}

function sendGreeting() {
    console.log(`Hello ${this.getFullName()}`);
}

let user = new User("mars", "quai");

// [ 1 ] callを使った呼び出し
sendGreeting.call(user);

// [ 2 ] applyを使った呼び出し
sendGreeting.apply(user);

// [ 3 ] bindを使った呼び出し
const userSendGreeting = sendGreeting.bind(user);
userSendGreeting();

thisはしっかり理解すれば、この様に便利に使うことができます。

基本的に自身が作った関数の使用法を理解して、毎回正しくObjectをバインドすれば問題ないのですが、

同じチームの誰か(あるいは数年後の自分)が次のようなコードを書いてしまうかも知れません。

sendGreeting(); // Error

このコードはgetFullName()が実装されたObjectがバインドされていないためエラーになるのですが...、

残念な事にそのエラーはトランスパイル後のコード実行時にやっと出力されるのです。

thisの型指定

TypeScriptでは関数のthisに型の成約を加えることができます。

関数の第1引数にthisを指定して、そこで型の指定をすることができます。

// Interfaceを定義
interface Person {
    firstName: string;
    lastName: string;
    getFullName(): string;
}

// Interfaceを継承
class User implements Person {
    public readonly firstName: string;
    public readonly lastName: string;
    constructor(firstName: string, lastName: string) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName(): string {
        return `${this.firstName} ${this.lastName}`;
    }
}

// this
function sendGreeting(this: Person) {
    console.log(`Hello ${this.getFullName()}`);
}

ここではInterfaceとしてPersonを定義し、更にそのInterfaceをthisの型成約に追加しました。この引数のthisはバインドなどのthisを割り当てる場合にのみ使用され、通常の呼び出しで渡す引数では無視されます。

thisを明確に定義することで、編集時のエディタの機能、またはトランスパイル時にthisの違法な割り当てをエラーとして出力してくれます。

試しにsendGreeting()をthisの割り当てなしで呼んでみましょう。

sendGreeting();  // 型 'void' の 'this' コンテキストを型 'Person' のメソッドの 'this' に割り当てることはできません。ts(2684)

thisの型指定の強制

上の節でthisの明確な型指定のメリットを理解できたと思います。

さらに気になる点があるすると、コード上に暗黙的なthisと型指定をするthisが混在することでしょう。

tsconfig.json"noImplicitThis": trueの設定を入れることでグローバル関数の様な暗黙的なthisの型推定を禁止することができます。

tsconfig.json
{
    "compilerOptions": {
        "noImplicitThis": true
    }
}
function sendGreeting() {
    console.log(`Hello ${this.getFullName()}`);  // [Error] 'this' は型として注釈を持たないため、暗黙的に型 'any' になります。
}

さいごに

参考URL

このエントリーをはてなブックマークに追加


関連記事

記事へのコメント