将键入添加到对象包装器

时间:2018-01-02 22:14:04

标签: typescript

我试图将类型添加到包装对象和数组的类中。我能够轻松地为物体做到这一点。

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[]

不兼容,因此不应该发生的事情

所以我想知道这个问题是否有更好的解决方案。

1 个答案:

答案 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 parameterA,这意味着您只能在一个数组包装器的对象上调用它。

嗯,实际上,编译器很难在结构上区分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参数不适用。

好的,希望有所帮助。祝你好运!