Something unexpected happen when I use Omit to get rid of the member of source interface for extension.
If I extend A
without omit, both a1
and a2
props are required. But I want to have a2
not present in B
but inherit all other members from type A
.
When I use Omit, somehow even a1 become optional, but I expect to a1
stay required.
interface A extends Record<string, any> {
a1: string;
a2: number;
}
interface B extends Omit<A, 'a2'>{
b1: string;
b2: number;
}
const example: B = {
// a1: 'dfdf', // should throw error, a1 is required
b1: 'string',
b2: 12,
}
Thanks for any hints 🙏
You can use key remapping to implement your own version of the Omit<T, K>
utility type that respects index signatures, as described in microsoft/TypeScript#49656:
type Oops = Omit<A, "a2">;
/* type Oops = {
[x: string]: any;
[x: number]: any;
} // ☹ */
type MyOmit<T, K extends PropertyKey> =
{ [P in keyof T as Exclude<P, K>]: T[P] }
type Okay = MyOmit<A, "a2">;
/* type Okay = {
[x: string]: any;
a1: string;
} // 🙂 */
The Omit<T, K>
utility type was introduced before key remapping was available.
There is an issue at microsoft/TypeScript#41383 suggesting that key remapping be used instead, but it's unlikely to happen in production (see this comment on ms/TS#49656).
Generally speaking once a utility type is introduced, it's not easy to redefine it without breaking real world code. Indeed there are several "problems" with Omit
that can't be addressed; index signatures is one issue, and distributing over unions is another (e.g., people expect Omit<T | U, K>
to be equivalent to Omit<T, K> | Omit<U, K>
, but it isn't). See microsoft/TypeScript#53169 for discussion of a potential fix for union distribution, along with all the problems that arise when trying to use it. All this means: if a TypeScript-provided utility type doesn't behave as you think it should, you should write your own utility type that does.