Kodokon kodokon.com

オーバーロード、型付き this、そして変性

オーバーロードの解決、this 擬似引数、そして関数の代入可能性を支配する変性の規則を理解する。

10 分 · 3 問

このレッスンを Kodokon で開く

オーバーロードされた関数は、複数の公開シグネチャのリストと、その後に続く単一の実装シグネチャを公開します。仕様上の重要な点: 後者は外部からは見えません。呼び出せるのは宣言されたオーバーロードだけです。解決はシグネチャを上から下へたどり、最初に互換性のあるものを採用します: 最も具体的なシグネチャを必ず先頭に置きましょう。さもないと決して選ばれません。

TYPESCRIPT
function parse(input: string): number;
function parse(input: string[]): number[];
function parse(input: string | string[]) {
  return Array.isArray(input)
    ? input.map((s) => s.length)
    : input.length;
}

const single = parse("abc");      // number
const many = parse(["a", "bb"]);  // number[]
各呼び出しが正確なオーバーロードに解決される。

TypeScript では、先頭に this 擬似引数を宣言できます。これは出力される JavaScript から完全に消去されます: それは呼び出しコンテキストに対する純粋に静的な制約であり、noImplicitThis が有効なときにチェックされます。ThisParameterTypeOmitThisParameter のユーティリティを使えばそれを抽出・削除でき、callapplybind とともに使われることを想定した関数を型付けするのに不可欠です。

TYPESCRIPT
function greet(this: { name: string }, msg: string) {
  return `${msg}, ${this.name}`;
}

type Ctx = ThisParameterType<typeof greet>;
// { name: string }
type Fn = OmitThisParameter<typeof greet>;
// (msg: string) => string

const user = { name: "Ada", greet };
user.greet("Hello"); // "Hello, Ada"

// greet("Hi"); // error: missing this context
this 引数は呼び出し箇所を制約するが、出力される JS には残らない。

変性は、型コンストラクタを通じて代入可能性がどのように伝播するかを表します。関数について、戻り値の型は共変であり、strictFunctionTypes を有効にすると、引数は反変になります: より広い型を受け付ける関数は、より狭い型を受け付ける関数の代わりになれますが、その逆は決してできません。意図的な例外が一つあります: 短縮構文で宣言されたメソッドの引数は双変のままです。これは歴史的な妥協であり、それがなければ Array<Dog>Array<Animal> に代入できなくなります。4.7 以降、inout で期待する変性を注釈でき(例: interface Producer<out T>)、コンパイラがそれを検証します。

TYPESCRIPT
interface Animal { name: string }
interface Dog extends Animal { bark(): void }

declare let f: (a: Dog) => void;
declare let g: (a: Animal) => void;

f = g; // OK: g accepts a wider type, that is safe
// g = f; // error with strictFunctionTypes

interface Sink { accept(a: Animal): void }
interface DogSink { accept(a: Dog): void }
declare let s: Sink;
declare let d: DogSink;

d = s; s = d; // both compile: bivariance!
厳格な関数型プロパティ、双変なメソッド。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. オーバーロードされた関数を呼び出すとき、コンパイラはどのシグネチャを選びますか?
    • 最も一般的なシグネチャ
    • 宣言順で最初に互換性のあるシグネチャ
    • 実装シグネチャ
    • すべてのシグネチャのユニオン
  2. 出力される JavaScript において、関数の this 引数はどうなりますか?
    • 本物の第一引数になる
    • 消える: それは純粋に静的な注釈である
    • bind への呼び出しに置き換えられる
    • 欠けているとランタイムエラーを引き起こす
  3. strictFunctionTypes のもとで、厳格な反変の引数チェックの恩恵を受けるのはどの構文ですか?
    • 短縮構文のメソッド、例えば m(a: X): void
    • 関数型のプロパティ、例えば m: (a: X) => void
    • 両方の構文
    • どちらでもない