#typescript #generics #advanced

Day 16: Generic Constraints

Limiting the “Any”

Welcome to Day 16! Yesterday we saw how Generics let you pass anything. But what if we need our generic to have a specific property, like .length?

function logLength<T>(arg: T): T {
  // console.log(arg.length); // Error: Property 'length' does not exist on type 'T'.
  return arg;
}

TypeScript complains because T could be a number, which doesn’t have .length.

The extends Keyword

We can constrain T to say “T must look at least like this interface”.

interface Lengthwise {
  length: number;
}

function logLength<T extends Lengthwise>(arg: T): T {
  console.log(arg.length); // OK!
  return arg;
}

Now usage:

logLength("Hello"); // OK (string has length)
logLength([1, 2, 3]); // OK (array has length)
// logLength(10); // Error: number doesn't have length

Using Keyof Constraint

A common pattern is ensuring a key exists on an object.

function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

let x = { a: 1, b: 2, c: 3 };

getProperty(x, "a"); // OK
// getProperty(x, "m"); // Error: "m" is not a key of x

Challenge for Today

  1. Create a generic function merge<T, U>(obj1: T, obj2: U).
  2. Constrain T and U to be objects (use extends object or similar).
  3. The function should return the intersection T & U.
  4. Implement it using Object.assign or spread syntax.

See you on Day 17 for Type Guards - checking types at runtime!