Advanced types section of TypeScript docs mentions some very useful built-in types as examples of conditional types and mapped types.
I was surprised to learn that there are more such types and some of them seem to be undocumented. This article contains a list of all such types.
The list is based on what I could find in es5.d.ts
on github.
List of types
Partial
Partial<T>
returns a type that has the same properties as T
but all of them are optional. This is mostly useful when strictNullChecks
flag is enabled.
Partial
works on a single level - it doesn't affect nested objects.
A common use case for Partial
is when you need to type a function that lets you override default values of properties of some object.
const defaultSettings: Settings = { /* ... */ };
function getSettings(custom: Partial<Settings>): Settings {
return { ...defaultSettings, ...custom };
}
Update:
However, this technique is not 100% type-safe. As pointed out by AngularBeginner, if custom
has a property that has been explicitly set to undefined
, the result will end up having this property undefined as well. Therefore, its type (Settings
) will be a lie.
A more type-safe version of getSettings
would look like this:
function getSettings(custom: Partial<Settings>): Partial<Settings> {
return { ...defaultSettings, ...custom };
}
Required
Required<T>
removes optionality from T
's properties. Again, you'll most likely need it if you have strictNullChecks
enabled (which you should 😉).
Similarly to Required
, Partial
works on the top level only.
The example is somehow symmetrical to the previous one. Here, we accept an object that has some optional properties. Then, we apply default values when a property is not present. The result is an object with no optional properties - Required<Settings>
.
function applySettings(settings: Settings) {
const actualSettings: Required<Settings> = {
width: settings.width || 100,
height: settings.height || 200,
title: settings.title || '',
}
// do something...
}
Readonly
This one you probably have heard of. Readonly<T>
returns a type that has the same properties as T
but they are all readonly
. It is extremally useful for functional programming because it lets you ensure immutability at compile time. An obvious example would be to use it for Redux state.
Once again, Readonly
doesn't affect nested objects.
Pick
Pick
lets you create a type that only has selected properties of another type.
An example would be letting the caller override only a specific subset of some default properties.
function updateSize(overrides: Pick<Settings, 'width' | 'height'>) {
return { ...defaultSettings, ...overrides};
}
Record
Record
lets you define a dictionary with keys belonging to a specific type.
JavaScript objects can be very naturally used as dictionaries. However, in TypeScript you usually work with objects using interfaces where the set of keys is predefined. You can work this around by writing something like:
interface Options {
[key: string]: string;
}
Record
lets you do this in a more concise way: type Options = Record<string, string>
.
Exclude
Exclude
makes a lot of sense when you look at types in terms of sets of possible values. For example, number
type can be looked at as a set containing all numerical numbers. A | B
is called a union because its set of possible values is a sum of possible values of A
and possible values of B
.
Exclude<T, U>
returns a type whose set of values is the same as the set of values of type T
but with all U
values removed. It is a bit like substruction, but defined on sets.
A good example of using Exclude
is to define Omit
. Omit
takes a type and its key and returns a type without this key.
interface Settings {
width: number;
height: number;
title: string;
}
type SizeSettings = Omit<Settings, 'title'>;
// type SizeSettings = {
// width: number;
// height: number;
// }
Omit<T, K>
can be defined by picking all keys from T
except K
. First, we'll Exclude
K
from the set of keys of T
. Next, we will use this set of keys to Pick
from T
.
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
Extract
Extract<T, U>
return those types included in T
that are assignable to U
. You can say that it returns a common part of T
and U
. However, the types don't have to be exactly the same - it suffices that a type from T
is assignable to U
.
For example, you can use Extract
to filter out function types from a union type:
type Functions = Extract<string | number | (() => void), Function>; // () => void
NonNullable
NonNullable<T>
removes null
and undefined
from the set of possible values of T
.
It is mostly useful when working with strictNullChecks
and optional properties and arguments. It has no effect on a type that is already not nullable.
type Foo = NonNullable<string | null | undefined>; // string
You can find a good usage example of NonNullable
in my previous article.
Parameters
This useful type returns a tuple of types of parameters of given function.
function fetchData(id: number, filter: string) {
}
type FetchDataParams = Parameters<typeof fetchData>; // [number, string]
type IdType = FetchDataParams[0]; // number
One interesting usage is typing wrapper functions without having to repeat the parameter list.
function fetchDataLogged(...params: Parameters<typeof fetchData>) {
console.log('calling fetchData');
fetchData(...params);
}
ConstructorParameters
ConstructorParameters
is exactly the same as Parameters
but works with constructor functions.
class Foo {
constructor(a: string, b: number) {}
}
type FooConstructorParams = ConstructorParameters<typeof Foo>;
One caveat is that you have to remember about typeof
in front of the class name.
ReturnType
The name is pretty self-explanatory - it returns a type returned by given function. I found this type really useful.
One example is in Redux where you define action creators and reducers. A reducer accepts a state object and an action object. You can use ReturnType
of the action creator to type the action object.
function fetchDataSuccess(data: string[]) {
return {
type: 'fetchDataSuccess',
payload: data
}
}
function reduceFetchDataSuccess(state: State, { payload }: ReturnType<typeof fetchDataSuccess>) {
}
InstanceType
InstanceType
is an interesting one. You can say that it is complimentary to typeof
operator.
It accepts a type of a constructor function and returns an instance type of this function.
In TypeScript, class C
defines two things:
- a constructor function
C
for creating new instances of classC
- an interface for objects of class
C
- the instance type
typeof C
returns the type of the constructor function.
InstanceType<typeof C>
takes the constructor function and returns type of the instances produced by this function: C
.
class C {
}
type CInstance = InstanceType<typeof C>; // C
Want to learn more?
Did you like this TypeScript article? I bet you'll also like my course!
Top comments (0)