Flow Generic Constraints

Matthew Garcia

There are cases in Flow where you’ll want to create a generic function or class, but want to limit what can be passed as a type argument.

Where Generic Constraints are Useful

Let’s say you have a class that wraps a Map, but makes a shallow copy before inserting:

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);
  }
}

Flow won’t let you do this, since, while objects can be spread, other types can’t:

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

And there’s nothing saying T has to be an object.

Adding Something that Says `T` Has to Be An Object

It’s pretty simple; just add a colon and the constraining type after the generic type declaration:

// `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);
  }
}

More Specific Constraints

Constraints can be more specific, allowing more complex interactions with generic types, since they fit some criteria. For example, a generic map that uses a property of T to determine the key:

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);
  }
}
  Tweet It
✖ Clear

🕵 Search Results

🔎 Searching...