Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I exclude an index signature from a type? [duplicate]

Tags:

typescript

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.

like image 523
Talbot White Avatar asked Jul 12 '18 05:07

Talbot White


1 Answers

Answer

UPDATE: As of TS 4.1 there is a real solution: How to remove index signature using mapped types

Alternative

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

More

PS: try not to mix index signatures with valid prop at root level. Instead use the nested object pattern

like image 115
basarat Avatar answered Oct 11 '22 10:10

basarat