Mix and match types in a Typescript function

Typescript provides a convenient way to accept different types of arguments for a function, or to combine multiple types into one super type.

Consider this example -

function printThis(a: string) {
  return typeof a;
}

console.log(printThis("hello")); // string

This function is fine if we want to return the input string for some strange reason. But, what if we want to generalise that function to - say, numbers?

Union type

In Typescript we can simply specify the argument as being string or a number using what is called “union type”.

function printThis(a: string | number) {
  return typeof a;
}

console.log(printThis("hello")); // string
console.log(printThis(1)); // number

The | specifies that the function accepts a string or a number. The function body has to handle any type-specific processing - here we are just returning the input and don’t quite bother with that.

Intersection type

Complementing the ‘union’ feature is what is called “intersection type”.

Intersection types allow us to combine multiple types in one and access any of the features of the individual types.

Consider a different example of two totally real-world classes -

class Tomato {
  constructor(public sweet: string) {}
}
class Chilly {
  constructor(public hot: string) {}
}

Since the classes are freaks of nature by themselves, we want to combine them and have the ability to make use of either properties or methods.

function combine<Tomato, Chilly>(tom: Tomato, chill: Chilly): Tomato & Chilly {
  const mix: Partial<Tomato & Chilly> = {};

  for (const prop in tom) {
    if (tom.hasOwnProperty(prop)) {
      (mix as Tomato)[prop] = tom[prop];
    }
  }
  for (const prop in chill) {
    if (chill.hasOwnProperty(prop)) {
      (mix as Chilly)[prop] = chill[prop];
    }
  }

  return mix as Tomato & Chilly;
}

By using a Tomato & Chilly we are instructing Typescript that “all types herein belongs to us”. In the function, we are simply extracting props from both of the supplied objects and combining them into one object of one type.

We can use the function like so -

const tom = new Tomato("sweet");
const chill = new Chilly("hot");

const rightMix = combine(tom, chill);

console.log(rightMix);
// { sweet: 'sweet', hot: 'hot' }

This is a rather simplistic example involving only props. In the real, real world we could use interfaces and also transfer all the goodness of member functions to the combined entity.

comments powered by Disqus