Problem Statement
While working on a project. I came across a use case where I need to create a type that needs to be a subtype of a given type including only a particular key.
const type Address = {
Id: string,
name: string,
surname: string,
country: string,
state: string,
landmark: string,
pincode: number,
isHomeAddress: boolean,
isRentedAddress: boolean
}
So, now I want to create a subtype from the above Address type which should only include keys of type boolean.
const type Address ={
isHomeAddress: boolean,
isRentedAddress: boolean
}
Solution
The Typescript Mapped Types comes in handy to resolve this kind of problem. Where you want to create a new type transforming a given type.
You can use mapped types to create a type out of the giving type having all of its properties as optional or boolean etc.
Now back to solving our problem.
So, we have our parent type as
type Address = {
Id: string,
name: string,
surname: string,
country: string,
state: string,
landmark: string,
pincode: number,
isHomeAddress: boolean,
isRentedAddress: boolean,
}
and our expected result should be
type OtherInfo = {
isHomeAddress: boolean,
isRentedAddress: boolean,
}
Let's solve this problem step by step.
1) Remove unwanted types
First of all, we will mark the unwanted types from the Address as never
type.
We use conditional types for it.
type RequiredTypes<T, C> = {
[Key in keyof T]: T[Key] extends C ? T[Key] : never
}
The above code will check which type in Address type extends the condition(C). It'll assign never type to all other keys not meeting our criteria.
never is the opposite of any type which means nothing can be assigned to it
The output of the above code will be.
Usage
type MarkAsNever = RequiredTypes<Address, boolean>
The MarkAsNever
type will look like this
type MarkAsNever = {
Id: never,
name: never,
surname: never,
country: never,
state: never,
landmark: never,
pincode: never,
isHomeAddress: boolean,
isRentedAddress: boolean,
}
As you can see the RequiredTypes
type has been assigned never to all other types except for isHomeAddress
and isRentedAddress
2) Removing keys with type never
Now we are on the last step which is to remove the type with never. For doing that we will use as clause introduced in typescript 4.1.
as clause has a nice feature by which you can filter out keys by producing never via conditional props.
type RemoveNeverField<T> = { [P in keyof T as T[P] extends never ? never : P]: T[P] };
In the above code we are looping through all the keys of T and if the type of the key is never our condition will return never which will be automatically ignored by the as a clause. So, in the end, we will only have non-never keys.
usage
type OnlyBoolean = RemoveNeverField<onlyBoolean>
Now the OnlyBoolean
type will look like below. Just how we wanted it. :ok_hand:
type OnlyBoolean = {
isHomeAddress: boolean,
isRentedAddress: boolean,
}
let's summarize all the required steps
1.
type RequiredTypes<T, C> = {
[Key in keyof T]: T[Key] extends C ? T[Key] : never
}
2.
type RemoveNeverField<T> = { [P in keyof T as T[P] extends never ? never : P]: T[P] };
You can further shorten the syntax. By directly using RequiredTypes
code inside RemoveNeverField
by creating a new type let's say PickByType
.
type PickByType<T, C> = RemoveNeverField<{
[Key in keyof T]: T[Key] extends C ? T[Key] : never
}>
usage
PickByType<Address, boolean>
Congratulations we have created a type that can create a new type containing only a given type. I hope that you have learned something new today.