首页 > 解决方案 > 是否可以在 TypeScript 中使用可以根据输入/输出类型链接的通用装饰器?

问题描述

对于我们的一些集成,我们在代码库中有相当“模板化”的实现,可以方便地放入“管道和过滤器”模式恕我直言。

可以使“组件”看起来像以下类型:

class Component1<In, Out, Xin, Xout>
class Component2<Xin, Xout, Yin, Yout>
class Component3<Yin, Yout> // only has 2 params but could be <Yin, Yout, None, None> for a custom 'None' type

这个想法是有一些东西允许这些被“链接”以允许这样的事情:

const c1 = new Component1<A,B,C,D>(...) //perhaps pass the param types in constructor? Other options?
const c2 = new Component2<C,D,E,F>(...)
const c3 = new Component3<E,F, None, None>(...)

const chain = c1.andThen(c2).andThen(c3) // The "last" item in the chain would "always" be a component of type <X,Y, None, None>

chain.run() // Not sure if this is needed but to make it clear that something "runs" this chain

我想不出任何创建这些组件的“通用”方法,其中可以在编译时“定义”这种链接,以限制哪些组件可以与其他组件连接(即,输入/输出类型应该匹配)。因此c1只能跟在后面,c2但不能跟在后面,但后面不能跟c3任何东西c3

这甚至可能吗?有什么能让它足够接近吗?

(对于好奇的人:试图实现 Finagle 在 Scala 世界中提供的类似“可组合性”)

标签: javascripttypescriptdecoratorfinagle

解决方案


这是我所拥有的:

class Component<T, U> {
    constructor(private t: T, private u: U) {}
    andThen<V>(component: Component<U, V>): Component<U, V> {
        // implement andThen
        return component;
    }
    static run<T>(component: Component<T, null>) {
        // implement run
    }
}

type A = 'a'; const a: A = 'a';
type B = 'b'; const b: B = 'b';
type C = 'c'; const c: C = 'c';

const c1 = new Component<A, B>(a, b);
const c2 = new Component<B, C>(b, c);
const c3 = new Component<C, null>(c, null);

c2.andThen(c1); // TS2345: A is not assignable to B
Component.run(c1.andThen(c2)); // TS2345: Component<B,C> not assignable to Component<B,null>
Component.run(c1.andThen(c2).andThen(c3));

我已经简化了代码:<Xin, Xout, Yin, Yout><T,U>但这很容易适应。

按预期键入。在运行时,Component<...,X>.andThen(Component<Y,...>)被检测为无效(第一个TS2345)。

Component稍微重构一下,调用.run的不是链本身(即.runComponent<..., null>)。

相反,我把它run作为一个静态方法,Component它只将最后一个组件作为输入。用法在最后两行演示

最后但同样重要的是,该类一直保持非常通用和多态,因此可以链接许多组件!

(new Component<'a', 'b'>('a', 'b'))
.andThen(new Component<'b', 'c'>('b', 'c'))
.andThen(new Component<'c', 'd'>('c', 'd'))
.andThen(new Component<'d', 'e'>('d', 'e'))
.andThen(new Component<'e', 'f'>('e', 'f'))
.andThen(new Component<'f', 'g'>('f', 'g'))

我希望这就是你要找的。


推荐阅读