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 therefore 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 officialPartial<T>utility type!