Flow には、ジェネリック関数またはクラスを作成したいが、型引数として渡すことができるものを制限したい場合があります。

一般的な制約が役立つ場合

Mapをラップするクラスがあるが、挿入する前に浅いコピーを作成するとします。

class ShallowMap<T> {
  map: Map<string, T>;

  constructor() {
    this.map = new Map();
  }

  get(key: string) {
    return this.map.get(key);
  }

  set(key: string, value: T) {
    // Make a shallow copy.  Flow will give an error on this line.
    const copy = {...value};
    this.map.set(key, copy);
  }
}

フローではこれを行うことはできません。オブジェクトを広げることはできますが、他のタイプではできないためです。

// This works.
const spreadEmptyObject = {...Object.create(null)};
const spreadObjectWithProperty = {...{id: 2}};
// This doesn't.
const spreadNumber = {...0};
const spreadString = {...''};

そして、Tがオブジェクトでなければならないということは何もありません。

`T`はオブジェクトでなければならないという何かを追加する

とても簡単です。 ジェネリック型宣言の後にコロンと制約型を追加するだけです。

// `T` has to be an object.
class ShallowMap<T: Object> {
  map: Map<string, T>;

  constructor() {
    this.map = new Map();
  }

  get(key: string) {
    return this.map.get(key);
  }

  set(key: string, value: T) {
    // Flow is okay with this line now.
    const copy = {...value};
    this.map.set(key, copy);
  }
}

より具体的な制約

制約はより具体的にすることができ、いくつかの基準に適合するため、ジェネリック型とのより複雑な相互作用が可能になります。 たとえば、Tのプロパティを使用してキーを決定する一般的なマップ:

type Keyable = {
  key: string,
};

// `T` has a string property called `key`.
class AutoKeyMap<T: {key: string}> {
  map: Map<string, T>;

  constructor() {
    this.map = new Map();
  }

  get(key: string) {
    return this.map.get(key);
  }

  set(value: T) {
    // Since `T` has a string property `key`, we can access it.
    const key = value.key;
    this.map.set(key, value);
  }
}