In TypeScript, what do "extends keyof" and "in keyof" mean?

Solution 1:

For any type T, keyof T is the union of known, public property names of T.

Example:

interface Person {
  age: number;
  name: string;
}

type PersonKeys = keyof Person; // "age" | "name"

Your assumption that keyof string yields startsWith | endsWith | trim | ... is therefore correct. You can learn more about it in the lookup type release notes.

extends keyof

extends, in this case, is used to constrain the type of a generic parameter. Example:

<T, K extends keyof T>

K can therefor only be a public property name of T. It has nothing to do with extending a type or inheritance, contrary to extending interfaces.

A usage of extends keyof could be the following:

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

const person: Person = {
  age: 22,
  name: "Tobias",
};

// name is a property of person
// --> no error
const name = getProperty(person, "name");

// gender is not a property of person
// --> error
const gender = getProperty(person, "gender");

Aside from the documentation on index types, I found this helpful article.

in keyof

in is used when we're defining an index signature that we want to type with a union of string, number or symbol literals. In combination with keyof we can use it to create a so called mapped type, which re-maps all properties of the original type.

A usage of in keyof could be the following:

type Optional<T> = { 
  [K in keyof T]?: T[K] 
};

const person: Optional<Person> = {
  name: "Tobias"
  // notice how I do not have to specify an age, 
  // since age's type is now mapped from 'number' to 'number?' 
  // and therefore becomes optional
};

Aside from the documentation on mapped types, I once again found this helpful article.

Fun fact: The Optional<T> type we've just built has the same signature as the official Partial<T> utility type!