UPDATE FOR TYPESCRIPT 4.1+
Since typeScript 4.1 introduced key remapping in mapped types, you can now write a non-ugly version of Omit
which handles types with index signatures more gracefully:
type NewOmit<T, K extends PropertyKey> =
{ [P in keyof T as Exclude<P, K>]: T[P] };
You can verify that it works as desired:
type RemoveId = NewOmit<Thing, "id">;
/* type RemoveId = {
[x: string]: any;
name: string;
description: string;
} */
Nice!
Playground link to code
PRE-TYPESCRIPT 4.1 ANSWER
You can make an Omit<>
that handles index signatures, but it's really ugly.
As noted, keyof T
if T
has a string index signature is string | number
, and any literal keys (like 'id'
) get eaten up. It is possible to get the literal keys of a type with an index signature, by doing some crazy type-system hoop jumping. Once you have the literal keys and the index signature keys, you can build up an Omit
:
type _LiteralKeys<T> = {
[K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;
type LiteralKeys<T> = _LiteralKeys<T> extends infer K ?
[K] extends [keyof T] ? K : never : never;
type Lookup<T, K> = K extends keyof T ? T[K] : never;
type _IndexKey<T> = string extends keyof T ?
Lookup<T, string> extends Lookup<T, number> ?
string : (string | number) : number extends keyof T ? number : never;
type IndexKey<T> = _IndexKey<T> extends infer K ?
[K] extends [keyof T] ? K : never : never;
type WidenStringIndex<T extends keyof any> =
string extends T ? (string | number) : T;
type Omit<T, K extends keyof T,
O = Pick<T, Exclude<
LiteralKeys<T>, K>> & Pick<T, Exclude<IndexKey<T>, WidenStringIndex<K>>>
> = { [P in keyof O]: O[P] }
I said it was ugly.
You can verify that it behaves as you expected:
type RemoveId = Omit<Thing, 'id'>;
// type RemoveId = {
// [x: string]: any;
// name: string;
// description: string;
// }
If you want to take the above Omit
, shove it in some library where nobody has to look at it, and use it in your code; that's up to you. I don't know that it handles all possible edge cases, or even what the "right" behavior is in some instances (e.g., you can have both a string
and a number
index where the number
values are narrower than the string
values... what do you want to see if you Omit<T, string>
on such a type? ♂️) So proceed at your own risk. But I just wanted to note that a solution of sorts is possible.
Okay, hope that helps. Good luck!