十字路口Map<CatId, Cat> & Map<DogId, Dog>
should 概念上足以给你你想要的行为,但在实践中这是行不通的。函数类型的交集产生超载 https://www.typescriptlang.org/docs/handbook/functions.html#overloads,并且解决了 TypeScript 中的重载调用签名分别地。它们不会组合在一起以允许单个调用调用两个调用签名(请参阅微软/TypeScript#14107 https://github.com/microsoft/TypeScript/issues/14107)。所以只要Map<CatId, Cat> & Map<DogId, Dog>
,你不能打电话animals.get(1 as AnimalId)
; an AnimalId
既不是CatId
nor a DogId
,根据两个单独的调用签名中的每一个的要求。
为了解决这个问题,您显然添加了“缺失”Map<AnimalId, Animal>
。不幸的是,这太过分了。你不仅得到想要的get()
行为,你得到不受欢迎的 set()
行为。自从cat.id
is an AnimalId
, and dog
is an Animal
, a Map<AnimalId, Animal>
肯定会让你打电话animals.set(cat.id, dog)
。我不会深入探讨协变和逆变的迂腐细节,但一般来说,如果阅读接受事物并集,那么writing应该接受交叉点-事物,而不是工会。所以唯一的方法是Map<AnimalId, Animal>
you'd like支持的是涉及reading内容。
对我们来说幸运的是,有一个ReadonlyMapTypeScript 标准库中定义的接口 https://github.com/microsoft/TypeScript/blob/d7d8f33def2affef86ce1d0f9fb604523ef515ca/lib/lib.es2015.collection.d.ts#L38-L43这就是为了这个目的。所以我倾向于注释animals
像这样:
const animals: Map<CatId, Cat> & Map<DogId, Dog>
& ReadonlyMap<AnimalId, Animal> = new Map();
一旦你这样做了,你就会得到你正在寻找的行为,至少对于你的示例代码来说:
animals.set(cat.id, cat) // okay
animals.set(cat.id, dog) // error, no overload matches this call
const test1: Cat | undefined = animals.get(cat.id) // okay
const test2: Dog | undefined = animals.get(dog.id) // okay
const test4: Animal | undefined = animals.get(1 as AnimalId) // okay
const test3 = animals.get(3) // error, number is not AnimalId
当然,可能还有该定义不支持的其他用例。重载确实有一些奇怪的怪癖。如果您真的很关心,您可能需要手写自己的界面,其行为完全符合您期望的多类型Map
行动。这是我之前针对不同案例做过的一个有趣的练习(请参阅这个问题 https://stackoverflow.com/questions/54907009/typescript-how-can-i-make-entries-in-an-es6-map-based-on-a-union-type)老实说,这并没有那么可怕。但我不会在这里讨论这一点,特别是如果上面的更简单的交集适用于您的用例。
Playground 代码链接 https://www.typescriptlang.org/play?#code/C4TwDgpgBA8mCGBHArhAPAFXBANFDA9gNYQB2UAvFMqUaQQO6kB8l+2UAZFAN4BQUKACcI8ACYFSAGxBQA+nIIIUEBQC58xMnwC+fPqEhQAgqQCWAW3hSAkmLYBheMDtQAPlAAiBAOZ39htCmltaOzu5evgEcTi72VHBIqGikyBYARhBCeLHM0UaxbPyCIuKSMlBEZqRiGgDksXUCwqIS0rJmtVCx-noGHN5+8bDKyakZWXiDef1Gg0XNpW0VVTX1g00lreUdXYO9+gDGkgDOwFCHzhqFVMWV1V0NznU4zZ0aAIxQ8CfdznavPrHUhnKASHwaea3ZqrR4bV6Cd5QAC0Xx+kSGgKOp3O8HMVikJw0AFl4GA0D0xDlnKxuKTyfsqRi8oJuAAlbbteloYIEgEmfHWVhUUgQBhQekACgAlABufR8PEhQkAOhOEGAksuwBVnTw2ulUAA9EaoMR4CBFYLVerNdrdUzwYaTVAskICNkoPQzQA3LJSAjiKBWYCHAAWEF+wDDZl+lykUj4wNBwEjwA+13CHhoYggADNqhBhkqCScVT4NVrnA7nabzZbk+dU2cAEyQ3wRHP5wvF61liua8E1411ogWpM4qDN4AAFg0vNC2Zq3dFveV-craN+C9sYlrZrHDcn04AzGwS9YN5qT-u3R68ONMkIoLGvQRzju7EA