Given an interface (from an existing .d.ts file that can't be changed):
interface Foo {
[key: string]: any;
bar(): void;
}
Is there a way to use mapped types (or another method) to derive a new type without the index signature? i.e. it only has the method bar(): void;
Edit: Since Typescript 4.1 there is a way of doing this directly with Key Remapping, avoiding the Pick
combinator. Please see the answer by Oleg where it's introduced.
type RemoveIndex<T> = { [ K in keyof T as string extends K ? never : number extends K ? never : K ] : T[K] };
It is based on the fact that 'a' extends string
but string
doesn't extends 'a'
.
There is also a way to express that with TypeScript 2.8's Conditional Types.
interface Foo { [key: string]: any; [key: number]: any; bar(): void; } type KnownKeys<T> = { [K in keyof T]: string extends K ? never : number extends K ? never : K } extends { [_ in keyof T]: infer U } ? U : never; type FooWithOnlyBar = Pick<Foo, KnownKeys<Foo>>;
You can make a generic out of that:
// Generic !!! type RemoveIndex<T extends Record<any,any>> = Pick<T, KnownKeys<T>>; type FooWithOnlyBar = RemoveIndex<Foo>;
For an explanation of why exactly KnownKeys<T>
works, see the following answer:
https://stackoverflow.com/a/51955852/2115619
With TypeScript v4.1 key remapping leads to a very concise solution.
At its core it uses a slightly modified logic from Mihail's answer: while a known key is a subtype of either string
or number
, the latter are not subtypes of the corresponding literal. On the other hand, string
is a union of all possible strings (the same holds true for number
) and thus is reflexive (type res = string extends string ? true : false; //true
holds).
This means you can resolve to never
every time the type string
or number
is assignable to the type of key, effectively filtering it out:
interface Foo {
[key: string]: any;
[key: number]: any;
bar(): void;
}
type RemoveIndex<T> = {
[ P in keyof T as string extends P ? never : number extends P ? never : P ] : T[P]
};
type FooWithOnlyBar = RemoveIndex<Foo>; //{ bar: () => void; }
Playground
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