type Foo<T> = {
// pretend we have a type definition for Q here
}[keyof T];
What does the
[keyof T]
do at the end there?
Short Answer
When placed at the end of a type declaration,
[keyof T]
creates an
indexed access type
in which the index to lookup is an
indexed type query
on
T
.
That is a mouthful. Here it is in two steps:
-
First create a union type of the property names of
T
. -
e.g.
"one" | "two" | "three"
-
then get the types of those properties from type
Q
. -
e.g.
string | number | boolean
keyof T
does the first step and
[]
does the second step.
Explanation
The
~ TypeScript 2.1
release notes say that an "
indexed type query
keyof T
yields the type of permitted property names for
T
." The use of the word "yield" makes an indexed type query sound like a
generator
of the properties names of
T
.
// given a type like this
interface Model {
one: string,
two: number,
three: boolean,
}
// the generator produces a union type of property names
type Props = keyof Model; // "one" | "two" | "three"
When
keyof T
is in brackets
[]
at the end of a type, it is acting as an
index access type
. An index access type gets the type of a property by name.
type TypeOfOne = Model["one"]; // string
type TypeOfTwo = Model["two"]; // number
type TypeOfThree = Model["three"]; // boolean
An index access type can also lookup the type of a union of property names :
// string | number | boolean
type TypeOfOneTwoOrThree = Model["one" | "two" | "three"];
That is what
{ /* some type Q */ }[keyof T]
is doing.
-
generate a union type of property names of
T
, -
turn that into a union type of those properties' types in
Q
.
Manual Type Expansion
// initial example
type Foo<T> = {
[K in keyof T]: T[K]
}[keyof T];
// usage
interface Model {
one: string,
two: number,
three: boolean,
}
// result
type FooModel = Foo<Model>; // string | number | boolean
Expanding the initial example manually looks like this:
// replace T with Model
type FooModel1 = {
[K in keyof Model]: Model[K]
}[keyof Model];
// replace keyof Model with the keys
type FooModel2 = {
[K in "one" | "two" | "three"]: Model[K]
}["one" | "two" | "three"];
// replace K with each key instance
type FooModel3 = {
one: Model["one"];
two: Model["two"];
three: Model["three"];
}["one" | "two" | "three"];
// replace Model["key"] with its type
type FooModel4 = {
one: string;
two: number;
three: boolean;
}["one" | "two" | "three"];
// expand the lookup type union
type FooModel6 = string | number | boolean;