在字典属性中推断通用类型

3

我有一个通用接口,其中我希望一个属性只包含另一个属性的键,并且具有相同的类型...

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

没问题,我以前做过类似的事情... 基本上我希望它能根据我传递给propA的内容来推断T是什么。

但是我的问题在于,我有一个包含许多IMyInterface类型的字典...

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

这里是我的问题:上面的定义要求使用一个泛型类型...但我使用的任何类型都会应用到每个属性。如果我使用 any,则会失去意义,因为在声明字典时它假设所有内容都是 any。

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中只允许具有字符串类型的name属性和数字类型的age属性。
如果不定义IMyDictionary类型,一次创建每个项目,然后将它们分配给新对象(myDictionary)可以很容易地实现此操作...
const test1: IMyInterface = {
    propA: objA,
    propB: { age: 40 }
}

const myDictionary = { test1 }

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

我无法找到一种方式声明IMyDictionary类型以强制它为IMyInterface的每个实例推断通用类型。或者,我可以接受以不同方式定义IMyInterface以推断propB的属性和类型的想法。

提前致谢!

1个回答

2
你需要的类型是一个字典,其中每个条目都是一种某些 IMyInterface<T> 类型,但不是任何特定的T。这可能最好表示为存在类型,例如(可能){[k: string]: IMyInterface<exists T>},它目前在TypeScript(以及大多数其他带有泛型的语言)中没有本地支持。TypeScript(以及大多数其他带有泛型的语言)只有通用类型:想要值为X<T>类型的人可以指定他们想要的任何类型的T,并且提供者必须能够遵守。存在类型是相反的:想要提供类似X<exists T>类型的值的人可以选择他们想要的任何特定类型的T,而该值的接收方只需遵守即可。存在类型让你将泛型类型“隐藏”在非泛型类型中。
然而,TypeScript没有存在类型,所以我们必须做些其他的事情。(好吧,它没有本地存在类型。您可以通过使用通用函数和通过回调来反转控制来模拟它们,但这比我接下来要建议的解决方案更加复杂。如果您仍然对存在类型感兴趣,可以阅读有关它的链接文章)
我们可以将IMyDictionary的形状描述为一个通用类型,我们不太关心它,并尽可能让编译器为我们推断。以下是一种方法:
type IMyDictionary<T> = { [K in keyof T]: IMyInterface<T[K]> }
const asIMyDictionary = <T>(d: IMyDictionary<T>) => d;

这个想法是,不要声明一个类型为IMyDictionary的变量,而是使用帮助函数asIMyDictionary()来获取值并生成适当的类型IMyDictionary<T>。顺便说一句,这只有在类型IMyDictionary<T>同态映射类型时才起作用,因此可以从类型IMyDictionary<T>的值中推断出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的函数或类型都必须变成泛型。也许这对你来说并不痛苦。如果你觉得痛苦,可以考虑“模拟”的存在类型。但是我已经写了很多了,希望以上内容能帮到你...如果你需要我写出“模拟”的存在版本,我可以为你写出来。
祝你好运!

这看起来是个很好的解决方案。只要我的赋值对其他开发人员来说不费力,我也不介意在我的定义中添加一些额外的泛型。我会在周一尝试一下,但它似乎相当牢靠。谢谢! - Tzannetos Philippakos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接