Exclude and Omit, by hand
Write two types. MyExclude<T, U> removes from the union T every member that is assignable to U. MyOmit<T, K> is T without the keys K, keeping readonly and optional as they are. Do not use the built-in Exclude, Omit or Pick.
Start from
// Remove from the union T every member assignable to U.
type MyExclude<T, U> = any;
// T without the keys K.
type MyOmit<T, K> = any;Not allowed here, so the type is written by hand: Exclude, Omit, Pick.
Hints
Each hint gives away a little more. Open one only when you need it.
Hint 1
A conditional type on a bare type parameter runs once for each member of a union: T extends U ? never : T.
Hint 2
For MyOmit, a mapped type over keyof T can drop a key by renaming it to never: [P in keyof T as P extends K ? never : P].
Solution
From here on the page gives the method away. If you are still solving, go back to the hints.
TypeScript
type MyExclude<T, U> = T extends U ? never : T;
type MyOmit<T, K extends PropertyKey> = {
[P in keyof T as P extends K ? never : P]: T[P];
};The checks it must pass
type _1 = Expect<Equal<MyExclude<"a" | "b" | "c", "a">, "b" | "c">>;
type _2 = Expect<Equal<MyExclude<string | number | boolean, string | boolean>, number>>;
type _3 = Expect<Equal<MyOmit<{ id: number; name: string; done: boolean }, "done">, { id: number; name: string }>>;
type _4 = Expect<Equal<MyOmit<{ id: number; name: string; done: boolean }, "id" | "done">, { name: string }>>;
type _5 = Expect<Equal<MyOmit<{ readonly id: number; name?: string; done: boolean }, "done">, { readonly id: number; name?: string }>>;What an interviewer looks for
It tests distributive conditional types and key remapping, the two ideas behind most utility types. Omit is used daily for component props.