推断字典属性中的泛型类型

时间:2019-05-10 14:22:10

标签: typescript

我有一个通用接口,在该接口中,我希望一个属性仅包含来自另一个属性的键,并且具有相同的类型。...

interface IMyInterface<T> {
    propA: T;
    propB: { [key in keyof T]?: T[key] };
}

这很好,我之前做过很多次类似的事情。...基本上,我希望它根据传递给propA的内容来推断T是什么。

但是,这是我的问题,我有一本字典,其中包含许多IMyInterface类型。...

interface IMyDictionary {
    [key: string]: IMyInterface;
}

这是我的问题所在,上面的定义需要一个泛型类型。...但是我使用的任何类型都将应用于每个属性。如果我使用除此以外的任何东西,这都是不对的,因为在声明我的字典时,它假定一切都是任意的。

interface IInterfaceA {
    name: string;
    age: number;
}

const objA: IInterfaceA = {
    name: 'John',
    age: 18
}

const myDictionary: IMyDictionary {
    test1: {
        propA: objA,
        propB: { age: 40 }
    }
}

因此,基本上,当将值分配给上述myDictionary时,我希望它推断propA为IInterfaceA类型,并且在propB中仅允许使用字符串类型的属性名称和使用数字类型的age。

通过不定义类型IMyDictionary,然后逐个创建每个项目,然后将它们分配给新对象(myDictionary),我可以轻松地做到这一点。

const test1: IMyInterface = {
    propA: objA,
    propB: { age: 40 }
}

const myDictionary = { test1 }

这将推断test1的通用类型,然后使用确切的属性名称而不是字符串数组来推断myDictionary的类型。但是如果可能的话,我宁愿使用普通的旧对象分配语法。

我想不出一种方法来声明IMyDictionary的类型,以强制它为每个IMyInterface实例推断通用类型。另外,我对定义IMyInterface来推断propB的属性和类型的不同方法持开放态度。

谢谢!

1 个答案:

答案 0 :(得分:2)

您要查找的类型是字典,其中每个条目都是 some IMyInterface<T>类型,但没有任何 specific T类型。最好用{[k: string]: IMyInterface<exists T>}之类的existential type来表示,目前TypeScript(其他大多数带有泛型的语言)都不支持本机。 TypeScript(以及大多数其他具有泛型的语言)仅具有 universal 类型:想要一个X<T>类型的值的人可以为他们想要的T指定任何类型,并且提供程序价值必须能够遵守。存在类型则相反:有人想要提供诸如X<exists T>之类的类型的值,可以为他们想要的T选择任何特定类型,而该值的接收者只需遵守即可。现有类型使您可以“隐藏”非通用类型内部的通用类型。

但是,TypeScript没有存在性类型,因此我们将不得不做其他事情。 (嗯,它没有 native 存在类型。您可以通过使用泛型函数并通过回调反转控制来emulate them,但使用起来比我要解决的方案还要复杂。提出下一步建议。如果您仍然对存在性感兴趣,可以阅读有关它的链接文章。


我们要做的第二件事是将IMyDictionary的形状描述为通用类型,而我们实际上并不关心它,并让编译器尽可能地为我们推断它。这是一种方法:

type IMyDictionary<T> = { [K in keyof T]: IMyInterface<T[K]> }
const asIMyDictionary = <T>(d: IMyDictionary<T>) => d;

这个想法是,您可以使用辅助函数IMyDictionary来获取值并生成合适的类型asIMyDictionary(),而不是将变量声明为IMyDictionary<T>类型。顺便说一下,这仅起作用,因为类型IMyDictionary<T>homomorphic mapped type,所以可以从类型T的值推断出IMyDictionary<T>。让我们看看它的作用:

const myDictionary = asIMyDictionary({
  test1: {
    propA: { name: "John", age: 18 },
    propB: { age: 40 }
  },
  test2: {
    propA: { size: 10, color: "blue" },
    propB: { color: "purple" }
  },
  test3: {
    propA: { problem: true },
    propB: { oops: false } // error!
    //       ~~~~~~~~~~~~
    // 'oops' does not exist in type '{ problem?: boolean | undefined; }'
  }
});

这可以正常工作,并在您期望的地方给您错误。是的!

这里需要说明的是,您仍然需要拖延不需要的T类型。您希望仅处理具体IMyDictionary的任何函数或类型都必须成为通用的。也许这对您来说不是很痛苦。如果是这样,则可以考虑“模拟”的存在类型。但是我已经在这里写了很多东西,因此希望以上内容对您有帮助...如果您需要我写出模拟的存在版本,则可以。

祝你好运!