我一直在将我的项目从JavaScript转换为TypeScript,
但这种类型的注释令我难过。
我有一个Serializer
接口和一个用于组合这些接口的类,如下所示:
interface Serializer<T> {
serialize(value: T): ArrayBuffer
}
class UnionSerializer {
constructor(types) {
this.types = types
}
serialize(value) {
for (const type of this.types) {
try {
return type.serialize(value) //throws error if type can't serialize value
}
catch (e) {}
}
throw new Error('No type matched')
}
}
我如何根据UnionSerializer
编写Serializer
的类型声明?
显然它的类型取决于传递给它的types
数组。所以它有一个像:
interface UnionSerializerConstructor {
new <T1>([t1]: [Serializer<T1>]): Serializer<T1>
new <T1, T2>([t1, t2]: [Serializer<T1>, Serializer<T2>]): Serializer<T1 | T2>
//...
}
为UnionSerializer
类编写类型注释的最优雅方法是什么,理想情况下不会为types
数组的不同长度写出所有构造函数注释?
答案 0 :(得分:1)
new UnionSerializer([a,b])
的类型与new UnionSerializer([b,a])
的类型之间是否存在差异?鉴于UnionSerializerConstructor
的类型签名返回的Serializer<A | B>
与Serializer<B | A>
相同,它看起来并不真实。在那种情况下,好消息!您无需担心指定通用的任意长度元组类型,即can't do yet。相反,只需接受一个数组:
interface UnionSerializerConstructor {
new <T>(types: T[]): Serializer<T>
}
如果您将[a,b]
(其中a
是A
且b
是B
)传递给构造函数,则会推断{{1} {},A | B
,如你所愿。
现在,您希望传递给构造函数的值本身为T
个实例。在这种情况下,你应该这样做:
Serializer
我可以继续为您输入interface UnionSerializerConstructor {
new <T>(types: (Serializer<T>)[]): Serializer<T>
}
:
UnionSerializer
如果您需要我解释其中的任何内容,请告诉我。希望能帮助到你。祝你好运!
@csander said:
谢谢你的回答!这是我最初的解决方案。我遇到的问题是,并非我传入的所有类型都必须
class UnionSerializer<T> implements Serializer<T> { constructor(public types: (Serializer<T>)[]) { // next line is unnecessary with "public types" above // this.types = types } serialize(value: T): ArrayBuffer { for (const type of this.types) { try { return type.serialize(value) //throws error if type can't serialize value } catch (e) { } } throw new Error('No type matched') } }
具有相同的Serializer<T>
。例如,我可能有一个T
和一个Serializer<string>
,因此联合的所需Serializer<number>
将是T
,但原始string | number
的实现都不是Serializer
Serializer<string | number>
。那有意义吗?
这是一个很好的观点,我没有解决它。简短的回答是在TypeScript generic type parameters are covariant中,这意味着,事实上,您始终可以将Serializer<string|number>
值缩小为Serializer<string>
类型的值:
declare const numberOrStringSerializer: Serializer<number | string>;
const justStringSerializer: Serializer<string> = numberOrStringSerializer;
const justNumberSerializer: Serializer<number> = numberOrStringSerializer;
(这种类型的缩小通常是不合理的,但TypeScript是出于性能和开发人员方便的原因而做的。有possible fixes for it但它们还不是语言的一部分。)
话虽如此,UnionSerializer
构造函数的类型推断将not automatically infer the union type:
new UnionSerializer([justNumberSerializer, justStringSerializer]); // error
但您可以手动指定它:
new UnionSerializer<string|number>([justNumberSerializer, justStringSerializer]); // ok
这有点烦人,但它确实有效。 可能是改善这种体验的方法,但我现在还不确定。这有帮助吗?