Optional Variance Annotations for Type Parameters
TL;DR
変性に関して型の注釈をin outで指定できるようになった。
書き方
in
または out
を使って変性を注釈する。
開発者が型を読み解くヒントとなるものであって、指定によって挙動が変わるものではない。
type Provider<out T> = () => T;
type Consumer<in T> = (x: T) => void;
type Mapper<in T, out U> = (x: T) => U;
type Processor<in out T> = (x: T) => T;
in
はT型のパラメータを持つことを注釈する。
type Consumer<in T> = (x: T) => void;
out
はT型をリターンすることを注釈する。
type Provider<out T> = () => T;
注釈と型宣言が一致しない場合、エラーが得られる。
type Provider2<out T> = (x: T) => T; // ❌ ERROR T型をパラメータに取ることが宣言されていない
type Consumer2<in T> = (x: T) => T; // ❌ ERROR T型をリターンすることが宣言されていない
共変
out
で注釈した「T型のリターン」は共変の性質を持つ。
型そのもの+サブタイプを許容する。
class A { a() {} }
class B extends A { b() {} }
class C extends B { c() {} }
type Provider<out T> = () => T;
const provide:Provider<B> = () => new B();
const p1:A = provide(); // Aに対してB(サブタイプ)を代入する
const p2:B = provide(); // Bに対してB(型そのもの)を代入する
const p3:C = provide(); // Cに対してB(スーパータイプ)を代入する ❌ ERROR
反変
in
で注釈した「T型のパラメータ」は反変の性質を持つ。
型そのもの+スーパータイプを許容する。
class A { a() {} }
class B extends A { b() {} }
class C extends B { c() {} }
type Consumer<in T> = (x: T) => void;
const consume:Consumer<B> = (x:B) => void 0;
consume(new A()); // AをB(サブタイプ)として使用する ❌ ERROR
consume(new B()); // BをB(型そのもの)として使用する
consume(new C()); // CをB(スーパータイプ)として使用する
不変
in out
で注釈した「T型のパラメータ」「T型のリターン」は相互作用で不変の性質を持つ。
型そのものを許容する。
class A { a() {} }
class B extends A { b() {} }
class C extends B { c() {} }
type Processor<in out T> = (x: T) => T;
const process:Processor<B> = (x:B) => new B();
const pr1:A = process(new A()); // ❌ ERROR(パラメータ)
const pr2:B = process(new B());
const pr3:C = process(new C()); // ❌ ERROR(代入)
双変
関数パラメータの共変は strictFunctionChecks:false
にすると双変の性質に変わる。
型そのもの+サブタイプ+スーパータイプを許容する。
// tsconfig.json
strictFunctionChecks: true
class A { a() {} }
class B extends A { b() {} }
class C extends B { c() {} }
type TakeB = (arg: B) => void;
const t1: TakeB = (arg: A) => {};
const t2: TakeB = (arg: B) => {};
const t3: TakeB = (arg: C) => {}; // ❌ ERROR
// tsconfig.json
strictFunctionChecks: false
class A { a() {} }
class B extends A { b() {} }
class C extends B { c() {} }
type TakeB = (arg: B) => void;
const t1: TakeB = (arg: A) => {};
const t2: TakeB = (arg: B) => {};
const t3: TakeB = (arg: C) => {};
Last updated