如何从类型中排除索引签名?

时间:2018-07-12 05:13:44

标签: typescript

如果我按以下方式定义我的类型,那么它将遵循我的期望行为。

interface Foo {}

interface Bar {
    a: string;
    b: boolean;
    c: Foo;
}

type x = keyof Bar; // "a" | "b" | "c"

但是,如果我尝试添加索引签名,它将丢失我所有的预定义成员。

interface Bar {
    [index: string]: any;
}

type x = keyof Bar; // string | number

有没有办法在TypeScript中正确执行此操作?

类似的东西:

type x = Exclude<Bar, { [index: string]: any }>; // never

编辑 我尝试了类似于杰克的解决方案,并得到了这个:

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不起作用,由于某种原因,该类型的信息变为{}Foo2是有效的,但对于类型为Foo2的变量,intellisense不会说Foo2。相反,他们有Indexable<BaseFoo>

我真的很想尝试对用户隐藏这种类型的按摩。而且不幸的是,要求他们从Indexable<T>T来回投射是不可行的。

2 个答案:

答案 0 :(得分:0)

否,因为这是正确的行为。 string | "x"将简化为string,因为"x" extends string为真。仅定义string | number索引签名时会得到string,因为JavaScript将数字索引转换为对象上的字符串索引。

如果您要寻找所需的行为,则需要更改接口定义。

interface Foo {}

interface Bar {
    a: string;
    b: boolean;
    c: Foo;
}

interface IndexedBar extends Bar {
    [ key: string ]: any;
}

type x = keyof Bar; // "a" | "b" | "c"

还请注意,在某些情况下,您不会对IndexedBar进行正确的类型检查。

function setValue(obj: IndexedBar, key: string, value: any): void {
    obj[key] = value;
}

setValue(bar, "a", 4); // No error, even though a is explicitly a string.

答案 1 :(得分:0)

答案

对于具有索引签名的内容,没有通用的方法来执行。

替代

获取之前的键,添加索引签名:

interface Foo {}

interface BarCore {
    a: string;
    b: boolean;
    c: Foo;
}


type Bar = BarCore & {
    [index: string]: any;
}

type X = keyof BarCore; // a|b|c

更多

PS:尝试在根级别不要将索引签名与有效属性混合使用。而是使用nested object pattern