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