TypeScript 无法正确推断对象的类型 属性

TypeScript Does not Properly Infer the Type of an Object's Property

我正在尝试创建一个对象,其中每个 属性 都映射到一个 class 实例。 class 类型依赖于两个泛型。当我在我的对象中访问 属性 时,TypeScript 将 属性 的类型推断为用于定义对象的联合类型,而不是 属性 本身的类型。

如何让 TypeScript 正确推断 属性 的类型?

这里有一段代码可以帮助澄清问题:

export class CacheWithExpiry<T extends CacheTypes, V extends CacheNames> {
    constructor() {
    }
    ...
}

export type CacheNames =
  'foo_1' |
  'foo_2'

type CacheTypes = string | number

type CacheMap<T extends CacheTypes, V extends CacheNames> = { [key: string]: CacheWithExpiry<T, V> }

export const cacheMap: CacheMap<CacheTypes, CacheNames> = {
  foo1Cache: new CacheWithExpiry<string, 'foo_1'>(), // Type is inferred as expected 'CacheWithExpiry<string, "foo_1">'
  foo2Cache: new CacheWithExpiry<number, 'foo_2'>()
} as const

cacheMap.foo1Cache// Type is inferred as 'CacheWithExpiry<CacheTypes, CacheNames>' instead of 'CacheWithExpiry<string, "foo_1">'

编辑 1:我还需要维护 cacheMap 的类型安全,因此从 cacheMap 中完全删除类型是行不通的。示例:

export const cacheMap = {
  foo1Cache: new CacheWithExpiry<string, 'foo_1'>(),
  foo2Cache: new CacheWithExpiry<number, 'foo_2'>(),
  foo3: 'I am not a cache class instance', // I still want this to throw an error
} as const

您已将类型 CacheMap<CacheTypes, CacheNames> 分配给变量 cacheMap。此类型不包含有关键 foo1Cachefoo12ache 的任何信息。它所知道的是,可以有任何类型为 string 的键保存类型为 CacheWithExpiry<CacheTypes, CacheNames> 的值。这就是为什么当您访问 cacheMap 的 属性 时,TypeScript 不会知道具体的属性。

如果你去掉类型,TypeScript 将知道属性及其类型:

export const cacheMap = {
  foo1Cache: new CacheWithExpiry<string, 'foo_1'>(),
  foo2Cache: new CacheWithExpiry<number, 'foo_2'>()
} as const

cacheMap.foo1Cache // foo1Cache: CacheWithExpiry<string, "foo_1">

cacheMap 的类型现在是:

const cacheMap: {
    readonly foo1Cache: CacheWithExpiry<string, "foo_1">;
    readonly foo2Cache: CacheWithExpiry<number, "foo_2">;
}

Playground


如果在创建 cacheMap 对象时还需要类型验证,则需要将对象字面量传递给泛型函数。

function createCacheMap<T extends CacheMap<CacheTypes, CacheNames>>(cacheMap: T): T {
  return cacheMap
}

export const cacheMap = createCacheMap({
  foo1Cache: new CacheWithExpiry<string, 'foo_1'>(),
  foo2Cache: new CacheWithExpiry<number, 'foo_2'>(),
  anything: "string" // Error: Type 'string' is not assignable to type 'CacheWithExpiry<CacheTypes, CacheNames>
})

cacheMap.foo1Cache // foo1Cache: CacheWithExpiry<string, "foo_1">

Playground