如果我按以下方式定义我的类型,那么它将遵循我的期望行为。
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
来回投射是不可行的。
答案 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