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



如果我按如下方式定义我的类型,它会遵循我想要的行为。

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对于Foo2类型的变量。相反,他们有Indexable<BaseFoo>.

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

答案

更新:从TS 4.1开始,有一个真正的解决方案:如何使用映射类型删除索引签名

另类

在添加索引签名之前获取密钥:

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

type Bar = BarCore & {
[index: string]: any;
}
type X = keyof BarCore; // a|b|c

更多

PS:尽量不要将索引签名与根级别的有效 prop 混合使用。改用嵌套对象模式

不,因为这是正确的行为。string | "x"将简化为string"x" extends string因为这是真的。当您只定义string索引签名时,您会string | number,因为 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.

最新更新