As per the definition of the Pick @ typescriptlang.org, it constructs a new type for the mentioned properties only. Exclude @ typescriptlang.org is the opposite of it.
I've seen the following usage
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
But I don't quite get it. We could have simply used Exclude
to leave out the non-required fields and constructed a new type of it. Why the combined Pick
and Exclude
is used as Omit
?
Another example:
function removeName<Props extends ExtractName>( props: Props ): Pick<Props, Exclude<keyof Props, keyof ExtractName>> { const { name, ...rest } = props; // do something with name... return rest; }
Can't the return type of above be re-written with Exclude
as Exclude<Props, ExtractName>
?
The TypeScript Omit utility type Like the Pick type, the Omit can be used to modify an existing interface or type. However, this one works the other way around. It will remove the fields you defined. We want to remove the id field from our user object when we want to create a user.
Pick<Type, Keys> is a built-in utility type in TypeScript, which constructs a type by picking the set of properties Keys (string literal or union of string literals) from Type . type UserInfo = Pick<User, 'name' | 'age'> Now, the user is UserInfo type, which doesn't have a level property.
You are right about Pick
, it takes an object type and extracts the specified properties. So:
Pick<{ a: string, b:string }, 'a' > === { a: string }
The opposite of this is actually the later added Omit
. This type takes an object type and removes the specified properties from the type.
Omit<{ a: string, b:string }, 'a' > === { b: string }
Exclude
is a different beast, it takes a union type and removes a constituent of that union.
Exclude<string | number, string > === number
Exclude
is defined as:
type Exclude<T, U> = T extends U ? never : T;
This means that Exclude
will return never
if T
extends U
, and T
if it does not. So this means that:
Exclude<string, number>
is string
Exclude<string, string>
is never
The thing is that conditional types distribute over naked type parameters. So this means that if applied to a union we get the following:
Exclude<string | number, number> => Exclude<string, number> | Exclude<number, number> // Exclude distributes over string | number => string | never => // each application of Exclude resolved to either T or never => string // never in unions melts away
Exclude
is used in the definition of Omit
. Exclude<keyof T, K>
takes a union of the keys of T
and removes the keys specified by K
. And then Pick
extracts the remaining properties from T
.
Edit
While both Omit
and Exclude
take two type arguments (and no relationship is enforced between the two) they can not be use interchangeably. Look at the result of some applications of these types:
type T0 = Omit<{ a: string, b: string }, "a"> // { b: string; }, a is removed type T1 = Exclude<{ a: string, b: string }, "a"> // { a: string, b: string }, a does not extend { a: string, b: string } so Exclude does nothing type T2 = Omit<string | number, string> // Attempts to remove all string keys (basically all keys) from string | number , we get {} type T3 = Exclude<string | number, string> // string extends string so is removed from the union so we get number
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With