TypeScript provides sevral utility types. We can efficiently construct new types
with them. Omit<Type, Keys> is one of them to remove specific properties from
object types.
The basic usage is like this.
type NewType = Omit<OriginalType, "propertyToRemove">;
Pitfall
There is an pitfall of the use of Omit<Type, Keys>. Suppose you want to remove
the prop1 property from the Props interface bellow.
interface Props {
prop1: string;
prop2: number;
prop3: boolean;
[key: string]: any;
}
You might think that Omit<Type, Keys> can achieve that like this.
type PropsWithoutProp1 = Omit<Props, "prop1">;
However, the PropsWithoutProp1 type is interpreted as following...
type PropsWithoutProp1 = {
[x: string]: any;
[x: number]: any;
};
The prop2 and the prop3 properties are gone! This is not what we intended to
do!
Let's dive into Omit<Type, Keys> itself.
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Exclude<keyof Props, "prop1"> points to string | number because the Prop
type has an string index sigunature,
for more details.
Thus the string overlaps "prop2" | "prop3".
Solution
This is one of the solutions to preserve the prop2 and the prop3 properties
by
re-mapping keys.
type PropsWithoutProp1 = {
[K in keyof Props as K extends "prop1" ? never : K]: Props[K];
};
The resulting type is what we expected!
type PropsWithoutProp1 = {
[x: string]: any;
prop2: number;
prop3: boolean;
};
The K in keyof Props part iterates over
"prop1" | "prop2" | "prop3" | string, and the
as K extends "prop1" ? never : K part creates the resulting keys removing the
prop1 by setting the never in the true branch of the conditional type.