TypeScript 泛型:如何定义结构上与其他类型 S 相同的类型 T

TypeScript generics: how to define type T which is structurally the same as other type S

我有一个 Step 的概念,它需要 A 类型的值作为输入并给出 B.

类型的值
class Step<A, B> {
  constructor(private readonly f: (a: A) => B) { }

  public run(a: A): B {
    return this.f(a);
  }
}

现在我想组合两个步骤,所以我在里面得到了这样的东西 class Step:

  public andThen<C, D>(nextStep: Step<C, D>): Step<A, D> {
    return new Step<A, D>((state: A) => {
      const b: B = this.f(state);
      return nextStep.run(b);  // <---- compile error, B and C have no relation defined
    });
  }

我想要实现的是以某种方式告诉类型系统我们可以将类型 B 传递给需要类型 C 的函数(结构类型应该检查 C 出现在 B) 中,因此行 return nextStep.run(b) 工作正常。

示例:

const stepA: Step<{}, {a: number, b: string}> = new Step((input: {}) => ({ a: 5, b: "five" }));
const stepB: Step<{a: number}, {c: number}> = new Step((input: {a: number}) => ({c: input.a + 5}));

const steps = stepA.andThen(stepB)

如您所见,stepB 需要输入 {a: number},因此它可以从 stepA 得到输出 {a: number, b: string}。但是我不知道如何定义 andThen 中的关系。有什么想法可以实现吗?


type SubType<T, WiderT> = T extends WiderT ? WiderT : never;

class Step<A, B> {
  constructor(private readonly f: (a: A) => B) { }

  public run(a: A): B {
    return this.f(a);
  }

  public andThen<C, D>(nextStep: Step<SubType<B, C> | B, D>): Step<A, D> {
    return new Step<A, D>((state: A) => {
      const b = this.f(state);
      return nextStep.run(b);
    });
  }
}

const stepAB: Step<{}, {a: number, b: string}> = 
new Step((input) => ({ a: 5, b: "five" }));

const add5 = ({ a }: { a: number }) => ({ c: a + 5 });
const stepCD: Step<{ a: number }, { c: number }> = new Step(add5);


const stepAD = stepAB.andThen(stepCD);

核心点是SubType类型:

type SubType<T, WiderT> = T extends WiderT ? WiderT : never;

它说如果类型扩展了给定的类型那么它没问题,但它不是那么永远不会。注意使用方法:

SubType<B, C> | B

所以我们说如果类型 B 扩展 C,那么 B 是更具体的类型然后 C 那么我们允许,如果不是我们不由 never。此外,我们允许 B 本身(没有 union 仍然存在编译错误)