TypeScript类型注释表示可变数量的泛型?

时间:2017-08-17 03:05:37

标签: javascript typescript

我一直在将我的项目从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数组的不同长度写出所有构造函数注释?

1 个答案:

答案 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](其中aAbB)传递给构造函数,则会推断{{1} {},A | B,如你所愿。

现在,您希望传递给构造函数的值本身T个实例。在这种情况下,你应该这样做:

Serializer

我可以继续为您输入interface UnionSerializerConstructor { new <T>(types: (Serializer<T>)[]): Serializer<T> }

UnionSerializer

如果您需要我解释其中的任何内容,请告诉我。希望能帮助到你。祝你好运!

更新1

@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

这有点烦人,但它确实有效。 可能是改善这种体验的方法,但我现在还不确定。这有帮助吗?