我试图将类型添加到包装对象和数组的类中。我能够轻松地为物体做到这一点。
interface IObject1 {
value1: string,
}
interface IObject2 {
myObject: IObject1,
myObjects: IObject1[]
}
interface Wrapper<T>{
$<K extends keyof T>(
selection: K
): Wrapper<T[K]>;
}
const wrappedObject2: Wrapper<IObject2> = undefined as any;
//This correctly get the type Wrapper<IObject1>
const wrappedObject1 = wrappedObject2.$('myObject');
但是keyof不适用于数组索引,因此以下内容不起作用:
const wrappedObject1InsideArray = wrappedObject2.$('myObjects').$(1);
我能够使用以下代码,但我担心我可能会滥用打字稿:
interface IObject1 {
value1: string,
}
interface IObject2 {
myObject: IObject1,
myObjects: IObject1[]
}
type ValuesOf<T extends any[]> = T[number];
interface Wrapper<T extends any, U extends any[]= any[]>{
$(selection: number): Wrapper<ValuesOf<U>, ValuesOf<U>>;
$<K extends keyof T>(
selection: K
): Wrapper<T[K], T[K]>;
}
const wrappedObject2: Wrapper<IObject2> = undefined as any;
const wrappedObject1 = wrappedObject2.$('myObject');
const wrappedObject1InsideArray = wrappedObject2.$('myObjects').$(1);
我对此解决方案的不满意是wrappedObject1和wrappedObject1InsideArray的类型为Wrapper<IObject1, IObject1>
。由于IObject1与T extends any[]
所以我想知道这个问题是否有更好的解决方案。
答案 0 :(得分:1)
是的,似乎是it's difficult to get keyof T[]
to include number
。代替那个,怎么样:
interface Wrapper<T> {
$<K extends keyof T>(
selection: K
): Wrapper<T[K]>;
$<A>(this: Wrapper<A[]>, selection: number): Wrapper<A>
__brand?: T
}
我添加了$
的第二次重载。对于某些Wrapper<A[]>
,此函数的a this
parameter为A
,这意味着您只能在一个数组包装器的对象上调用它。
嗯,实际上,编译器很难在结构上区分Wrapper<IObject1>
和Wrapper<IObject1[]>
。我想这是因为唯一的区别在于$
签名的通用部分。所以为了解决这个问题,我添加了一个__brand
类型的可选幻像T
属性,它允许编译器明确地区分Wrapper<T>
和Wrapper<U>
T
之间的区别}和U
是不同的。该属性不需要在运行时存在,只需要在那里帮助编译器。
好的,让我们使用它:
const wrappedObject2: Wrapper<IObject2> = undefined as any;
const wrappedObject1 = wrappedObject2.$('myObject');
const expectedError = wrappedObject2.$('myObject').$(0); // IObject1 is not an array
const wrappedObject1InsideArray = wrappedObject2.$('myObjects').$(1);
一切都按预期工作,并注意如何阻止在expectedError
中调用第二个重载,因为this
参数不适用。
好的,希望有所帮助。祝你好运!