是否可以在TypeScript中使用按其输入/输出类型链接的通用修饰符?

时间:2019-03-11 18:59:20

标签: javascript typescript decorator finagle

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

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

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世界中提供的类似“可组合性”)

2 个答案:

答案 0 :(得分:1)

您对泛型的使用让我有些困惑,因为您似乎并没有弄清楚类型参数变量与插入它们的具体类型之间的区别。更不用说您使用诸如valNone之类的非TS术语。无论如何,以下是经过编译的内容,可能会为您提供所需的行为:

type NotNever<T, Y=T, N=never> = [T] extends [never] ? N : Y;

// just create types, don't worry about implementation
declare class BaseComponent<In, Out, Xin=never, Xout=never> {
  // make BaseComponent depend structurally on type parameters
  i: In;
  o: Out;
  xi: Xin;
  xo: Xout;

  // andThen() is generic, and only accepts the right kind of other component
  // only callable if Xin and Xout are *not* never
  andThen<Yin, Yout>(
    this: NotNever<Xin | Xout, this>,
    c: BaseComponent<Xin, Xout, Yin, Yout>
  ): BaseComponent<In, Out, Yin, Yout>;

  // run() is only callable if Xin and Xout *are* never
  run(this: BaseComponent<In, Out, never, never>): void;
}

// create some concrete subclasses where parameters are set with string literal types
class Component1 extends BaseComponent<'In', 'Out', 'Xin', 'Xout'> { }
class Component2 extends BaseComponent<'Xin', 'Xout', 'Yin', 'Yout'> { }
class Component3 extends BaseComponent<'Yin', 'Yout'> { }

您可以看到它的工作原理:

const c1 = new Component1();
const c2 = new Component2();
const c3 = new Component3();

c1.andThen(c1); // error
c1.andThen(c2); // okay
c1.andThen(c3); // error
c1.run(); // error

c2.andThen(c1); // error
c2.andThen(c2); // error
c2.andThen(c3); // okay
c2.run(); // error

c3.andThen(c1); // error
c3.andThen(c2); // error
c3.andThen(c3); // error
c3.run(); // okay

const chain = c1.andThen(c2).andThen(c3) // BaseComponent<'In', 'Out', never, never>;
chain.run(); // okay

我认为这类似于您想要的吗?希望能有所帮助;祝你好运!


编辑:执行相同操作但又不用担心conditional typespolymorphic this的另一种方法是:

// one base class for the end of the chain
declare class EndComponent<In, Out> {
  i: In;
  o: Out;
  run(): void;
}

// another base class for intermediate parts of the chain
declare class PipeComponent<In, Out, Xin, Xout> {
  i: In;
  o: Out;
  xi: Xin;
  xo: Xout;
  // andThen() is overloaded 
  andThen<Yin, Yout>(
    c: PipeComponent<Xin, Xout, Yin, Yout>
  ): PipeComponent<In, Out, Yin, Yout>;
  andThen(c: EndComponent<Xin, Xout>): EndComponent<In, Out>;
}

class Component1 extends PipeComponent<'In', 'Out', 'Xin', 'Xout'> { }
class Component2 extends PipeComponent<'Xin', 'Xout', 'Yin', 'Yout'> { }
class Component3 extends EndComponent<'Yin', 'Yout'> { }

其余部分的行为应与以前相同。再次祝你好运!

答案 1 :(得分:1)

这就是我所拥有的:

.backdropBackground {
  /* your css needs */
}

我已经简化了代码: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.run(即链的最后一个组成部分)调用。

相反,我将Component<..., null>用作run的静态方法,并且仅将 last组件作为输入。最后两行演示了用法

最后但并非最不重要的一点是,该类保持非常通用且多态,因此可以链接许多组件!

Component

我希望这就是您想要的。