【发布时间】:2022-01-06 17:09:48
【问题描述】:
我正在通过实现 Fantasy Land Spec 来探索 Typescript 类型系统,但在尝试实现 Semigroup 的规范时遇到了问题。
规范规定Semigroup 应遵守以下类型定义:
concat :: Semigroup a => a ~> a -> a
我理解这意味着实现Semigroup 的类型a 应该有一个concat 方法,该方法接受a 类型的参数并返回a 类型的参数。
我能想到在 TypeScript 中表达这种类型定义的唯一方法是:
interface Semigroup {
concat(other: this): this;
}
但是当我尝试在一个类上实现这个接口时,像这样:
class Sum implements Semigroup {
constructor(readonly num: number) {}
concat(other: Sum): Sum {
return new Sum(this.num + other.num);
}
}
我收到一个编译器错误,告诉我:
Property 'concat' in type 'Sum' is not assignable to the same property in base type 'Semigroup'.
Type '(other: Sum) => Sum' is not assignable to type '(other: this) => this'.
Type 'Sum' is not assignable to type 'this'.
'Sum' is assignable to the constraint of type 'this', but 'this' could be instantiated with a different subtype of constraint 'Sum'.(2416)
感谢this S/O 的回答,我想我明白了这个问题。
我认为编译器本质上是在告诉我:您的界面表明您应该采用具体类型为 this 的参数(在这种特殊情况下为 Sum),但扩展 Sum 的类可以也可以传入。
但是,我不知道如何解决它。也就是说,我不知道如何在 TypeScript 中表达Semigroup 的类型定义。如何从接口中引用实现类?
这是 TS Playground 的link。
更新
@Guerric P 的回答让我想到了部分解决方案。 Guerric 的解决方案是在接口上使用泛型。此解决方案使实施Semigroup 规范成为可能,如here 所示,但接口并没有真正执行它。
幻想大陆进一步描述了规格如下:
s.concat(b)
/**
* `b` must be a value of the same `Semigroup`
*
* If `b` is not the same semigroup, behaviour of `concat` is
* unspecified.
*
* `concat` must return a value of the same `Semigroup`.
*/
我认为我们至少可以将类型限制为Semigroup,而不是使b 成为泛型。这样,它强制执行b 必须是Semigroup 类型的约束,如下所示:
interface Semigroup {
concat(other: Semigroup): Semigroup;
}
但它仍然没有强制它必须是相同的Semigroup。我仍在寻找一种使用 TypeScript 类型系统的方法。
【问题讨论】:
标签: typescript interface concatenation this semigroup