我需要的是像 但所有值必须是任何一种特定类型 。 例如,不是一个值是Int,另一个值是Double,第三个值是String-所有记录都应具有相同的特定类型,并向下转换为Any。 我将把地图作为半类型不可知函数的参数,使用匹配/大小写确定类型并执行特定算法。 该函数实际上将支持某 ...
提示:本站收集StackOverFlow近2千万问答,支持中英文搜索,鼠标放在语句上弹窗显示对应的参考中文或英文, 本站还提供 中文繁体 英文版本 中英对照 版本,有任何建议请联系yoyou2525@163.com。
这是显示我想要实现的目标的示例。 除了两个问题外,它几乎可以工作:
基本上在我看来,我在这里所做的并不是真正的一件事。 所以问题是:有没有其他方法可以做同样的事情?
type Opaque<Type, Token = unknown> = Type & {
readonly __opaque__: Token
}
type AnimalId = CatId | DogId
type Animal = Cat | Dog
type CatId = Opaque<number, Cat>
type Cat = {
readonly kind: 'Cat'
readonly id: CatId
}
type DogId = Opaque<number, Dog>
type Dog = {
readonly kind: 'Dog'
readonly id: DogId
}
const cat: Cat = {
kind: 'Cat',
id: 1 as CatId,
}
const dog: Dog = {
kind: 'Dog',
id: -1 as DogId,
}
const animals: Map<CatId, Cat> & Map<DogId, Dog> & Map<AnimalId, Animal> = new Map()
animals.set(cat.id, cat) // no error should be here
animals.set(cat.id, dog) // wanna see error here
const test1: Cat | undefined = animals.get(cat.id) // no error should be here
const test2: Dog | undefined = animals.get(dog.id) // no error should be here
const test4: Animal | undefined = animals.get(1 as AnimalId) // no error should be here
const test3 = animals.get(3) // wanna see error here
交集Map<CatId, Cat> & Map<DogId, Dog>
概念上应该足以为您提供所需的行为,但实际上这不起作用。 函数类型的交集会产生重载,而 TypeScript 中的重载调用签名是单独解析的。 它们不组合以允许单个调用调用两个调用签名(请参阅microsoft/TypeScript#14107 )。 因此,仅使用Map<CatId, Cat> & Map<DogId, Dog>
,您无法调用animals.get(1 as AnimalId)
; AnimalId
既不是CatId
也不是DogId
,这是两个单独调用签名中的每一个所要求的。
为了解决这个问题,您显然已经添加了“缺失的” Map<AnimalId, Animal>
。 不幸的是,这太过分了。 您不仅会得到理想的get()
行为,还会得到不理想的set()
行为。 因为cat.id
是一个AnimalId
,而dog
是一个Animal
,所以Map<AnimalId, Animal>
肯定会允许你调用animals.set(cat.id, dog)
。 我不会深入探讨协变和逆变的迂腐细节,但一般来说,如果阅读接受事物的联合,那么写作应该接受事物的交集,而不是联合。 所以唯一的方法, Map<AnimalId, Animal>
你想支持是涉及阅读的内容的。
对我们来说幸运的是,TypeScript 标准库中定义了一个ReadonlyMap
接口,它正好用于此目的。 所以我倾向于像这样注释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
行为完全相同。 这是我之前针对不同案例做过的有趣练习(请参阅此问题),老实说,它并没有那么糟糕。 但我不会在这里深入讨论,尤其是如果上述更简单的交集适用于您的用例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.