9

Say I have

interface A {
  apple?: number;
  [key: string]: any;
}

interface B extends A {
  banana?: number;
}

I want a type C to extend all from A and all from B except [key: string]: any;. I want a type D to inherit all from B except all from A.

How do I make C and D in typescript?

Derek
  • 11,980
  • 26
  • 103
  • 162
  • Does this answer your question? [How to remove index signature using mapped types](https://stackoverflow.com/questions/51465182/how-to-remove-index-signature-using-mapped-types) – Gerrit0 Jul 05 '21 at 19:20
  • Accepted answer does not work since TS 4.3. @Derek mind unaccepting it, so I can delete the answer? I think there are now better solutions in linked duplicate posts. – ford04 Nov 24 '21 at 16:03

3 Answers3

9

Pick all from B, remove A index signature:

type C = Pick<B, KnownKeys<B>>;

Pick all from B, exclude all from A, remove A index signature:

type D = Omit<C, KnownKeys<A>>

Type KnownKeys<T> removes the index signature of T, using only the known property keys (credit goes to Mihail Malostanidis and his answers here and here):

type KnownKeys<T> = {
    [K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;

How do types C and D look like?

type C = {
    banana?: number | undefined;
    apple?: number | undefined;
}

type D = {
    banana?: number | undefined;
}

const c: C = {
    apple: 1,
    banana: 2,
}

const d: D = {
    banana: 2
}

Playground

ford04
  • 66,267
  • 20
  • 199
  • 171
2

This should work:

type NoStringIndex<T> = { [K in keyof T as string extends K ? never : K]: T[K] };

interface A {
  apple?: number;
  [key: string]: any;
}

type A2 = NoStringIndex<A>
ecklf
  • 31
  • 2
-1

I don't think you can do that in the way you think (exclude [key: string]: any;) in sense that to exclude a key you have to use the key or its value type.

Instead you can pick the properties you want:

type C = Pick<B, 'apple' | 'banana'>;
Richard Haddad
  • 904
  • 6
  • 13