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

generic illustration for: 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);

  }

}