TypeScript 泛型:如何定义类型 T,使其在结构上与其他类型 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 中定义这种关系。有人知道该如何实现吗?
答案#1
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;
它说如果类型继承给定类型,那么就可以,但如果不是则为 never。注意它的使用方式:
SubType<B, C> | B
所以我们是在说,如果类型B继承C,因此B比C更具体类型,那么我们允许这样做,如果不是则通过never不允许。此外,我们也允许B本身(没有联合类型时仍然会有编译错误)
