In TypeScript, why "extends keyof" is used to declare generic type parameters with constraints instead of using "in keyof"?

typescriptlang.org, in its article about Generics, explains 2 uses of the keyword “extends” when declaring type parameters.

  1. In Generic Constraints.
interface Lengthwise {
  length: number;
}
 
function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {
  console.log(arg.length);
  return arg;
}
  1. When declaring a type parameter that is constrained by another type parameter.
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key];
}
 
let x = { a: 1, b: 2, c: 3, d: 4 };
 
getProperty(x, "a");

getProperty(x, "m");
// Error: Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.

It makes sense to use the “extends” keyword when constraining a generic type so that it should have the properties and methods of the extended type. (See the 1st use above). This also matches the use of the “extends” keyword in inheritance in OOP.

But why is the “extends” keyword used in the 2nd instance above to constrain the generic type parameter “Key” so it should be a property of the type parameter “Type”? Why not use the keyword “in” as “in keyof Type”, which makes more sense?

Thank you in advance for your answers and comments!

Note: I know that “in keyof” is used when declaring index signatures as below.

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

My question is why not use the same when constraining a generic type parameter when it should be a property of another type parameter?

The use of extends in the second example is correct, and unfortunately, TypeScript does not support in in the context you’re asking about. The syntax Key extends keyof Type is specifically designed to ensure that Key is a valid key of the Type.

Using in is reserved for mapped types, as you’ve noted, and is not interchangeable with extends for generic constraints.

If you want to clarify the intent in comments or documentation, you might add a note, but the syntax itself cannot be changed. Here’s the existing code for reference:

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

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

getProperty(x, "a"); // Works

getProperty(x, "m"); // Error: Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.

In summary, the use of extends is necessary for generic type constraints in TypeScript, and in cannot be used in this context.