[英]In TypeScript, how do you express that a type T, declared inside an interface I, should be of whatever type U that will implement said interface I
I'm exploring the Typescript type system by implementing the Fantasy Land Spec and I ran into an issue while trying to implement the spec for Semigroup .我正在通过实现Fantasy Land Spec来探索 Typescript 类型系统,但在尝试实现Semigroup的规范时遇到了问题。
The spec stipulates that a Semigroup
should adhere to the following type definition:规范规定
Semigroup
应遵守以下类型定义:
concat :: Semigroup a => a ~> a -> a
I understand this to mean that a type a
, which implements Semigroup
, should have a concat
method that takes in a parameter of type a
and returns a parameter of type a
.我理解这意味着实现
Semigroup
的类型a
应该有一个concat
方法,该方法接受 a 类型a
参数并返回 a 类型a
参数。
The only way I could think of expressing this type definition in TypeScript is this:我能想到在 TypeScript 中表达这种类型定义的唯一方法是:
interface Semigroup {
concat(other: this): this;
}
But when I try to implement this interface on a class, like this:但是当我尝试在 class 上实现这个接口时,像这样:
class Sum implements Setoid, Semigroup {
constructor(readonly num: number) {}
concat(other: Sum): Sum {
return new Sum(this.num + other.num);
}
}
I get a compiler error telling me that:我收到一个编译器错误,告诉我:
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)
Thanks to this S/O answer, I think I understand the problem.感谢这个S/O 答案,我想我理解了这个问题。
I think the compiler is essentially telling me: your interface says that you should be taking a parameter that is of the concrete type this
( Sum
in this particular case), but a class that extends Sum
could also be passed in.我认为编译器本质上是在告诉我:您的界面说您应该采用具体类型的参数
this
(在这种特殊情况下为Sum
),但是也可以传入扩展Sum
的 class 。
However, I don't know how to fix it.但是,我不知道如何解决它。 That is, I don't know how to express the type definition for
Semigroup
in TypeScript.就是不知道
Semigroup
中Semigroup的类型定义怎么表达。 How does one express that a type T, declared inside an interface I, should be of whatever type U that will implement said interface I?如何表示在接口 I 中声明的类型 T 应该是实现所述接口 I 的任何类型 U?
I don't want to question your interpretation of that fantasy-land spec, which I admit I don't fully understand, so I'll assume your interpretation is right.我不想质疑您对幻想土地规范的解释,我承认我并不完全理解,所以我假设您的解释是正确的。
The problem is that your class
could be extended, so this
could refer to that extended class.问题是您的
class
可以扩展,所以this
可以指扩展的 class。 There is no such thing as final class
or equivalent in TypeScript. TypeScript 中没有
final class
或等效项。
Now let's suppose you have an ExtendedSum
class which extends Sum
.现在假设您有一个扩展
Sum
的ExtendedSum
class 。 Your equals
implementation still works because (other: Sum) => boolean
is assignable to (other: ExtendedSum) => boolean
.您的
equals
实现仍然有效,因为(other: Sum) => boolean
可分配给(other: ExtendedSum) => boolean
。 Indeed, a function that takes a Sum
as parameter can take an ExtendedSum
as well (structural typing principle).实际上,将
Sum
作为参数的 function 也可以采用ExtendedSum
(结构类型原则)。
However, your concat
implementation doesn't work because (other: Sum) => Sum
is not assignable to (other: ExtendedSum) => ExtendedSum
.但是,您的
concat
实现不起作用,因为(other: Sum) => Sum
不能分配给(other: ExtendedSum) => ExtendedSum
。 Indeed, a function that returns a Sum
is not assignable to a function that returns an ExtendedSum
because a Sum
is not necessarily an ExtendedSum
.实际上,返回
Sum
的 function 不能分配给返回ExtendedSum
的 function ,因为Sum
不一定是ExtendedSum
。
You could fix that with a generic typed interface:您可以使用通用类型接口解决此问题:
interface Semigroup<T> {
concat(other: T): T;
}
class Sum implements Setoid, Semigroup<Sum> {
constructor(readonly num: number) {}
equals(other: Sum): boolean {
return this.num === other.num;
}
concat(other: Sum): Sum {
return new Sum(this.num + other.num);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.