If I define my types as follows, it follows my desired behavior.
interface Foo {}
interface Bar {
a: string;
b: boolean;
c: Foo;
}
type x = keyof Bar; // "a" | "b" | "c"
However, if I try to add an index signature, it loses all my predefined members.
interface Bar {
[index: string]: any;
}
type x = keyof Bar; // string | number
Is there a way to do this properly in TypeScript?
Something similar to:
type x = Exclude<Bar, { [index: string]: any }>; // never
EDIT I tried something similar to Jake's solution and got this:
interface Indexable<T> {
[index: string]: any;
}
type BaseType<T> = T extends Indexable<infer U> ? U : never;
interface BaseFoo {
Name: string;
}
interface Foo1 extends Indexable<BaseFoo> {}
type Foo2 = Indexable<BaseFoo>;
type base1 = BaseType<Foo1>; // {}
type base2 = BaseType<Foo2>; // BaseFoo
Foo1
does not work, for some reason the type info for that becomes {}
.
Foo2
does work, but intellisense does not say Foo2
for variables of type Foo2
. They instead have Indexable<BaseFoo>
.
I would really like to try to hide this type massaging from my users. And unfortunately, it's not feasible to ask them to cast back and forth from Indexable<T>
to T
.
UPDATE: As of TS 4.1 there is a real solution: How to remove index signature using mapped types
Get the keys before adding the index signature:
interface Foo {}
interface BarCore {
a: string;
b: boolean;
c: Foo;
}
type Bar = BarCore & {
[index: string]: any;
}
type X = keyof BarCore; // a|b|c
PS: try not to mix index signatures with valid prop at root level. Instead use the nested object pattern
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