TypeScript 有多态性this types https://www.typescriptlang.org/docs/handbook/advanced-types.html#polymorphic-this-types,您使用的地方type named this
指您所说的“与自身相同的类型”。
interface Cloneable {
clone(): this;
}
在子类型的值中Cloneable
, 方式this
将与该子类型相同:
interface Foo extends Cloneable {
bar: string;
}
declare const foo: Foo;
const otherFoo = foo.clone();
otherFoo.bar.toUpperCase();
但请注意,当您尝试实际实施 Cloneable
,编译器将阻止任何试图返回除相同内容之外的内容的内容this
你已经拥有的对象。在你的class A
, 有时候是这样的:
clone() { // error! Type 'A' is not assignable to type 'this'.
return new A(this.a.slice()); // returns a deep copy of itself
}
这实际上是一个很好的错误,因为您无法选择子类型触底的位置。有人可以过来并扩展A
(或者提供一个类型的值A & SomethingElse
), and this
将随之缩小:
class B extends A {
z: string = "oopsie";
}
declare const b: B;
b.clone().z.toUpperCase();
If your clone()
中的方法A
没有预见到可能的子类型,例如B
,那么它将无法以类型安全的方式工作。无论如何,谈论一般如何处理这个问题可能是一个题外话,但是如果您并不真正关心可能的子类型并且只是希望编译器接受您的实现,那么您将需要一个类型断言 https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions,以及面对子类型时的一些注意事项:
clone() {
return new A(this.a.slice()) as this;
}
您正在谈论的另一种技术:
interface Cloneable<T extends Cloneable<T>> {
clone(): T;
}
叫做F-界多态性 https://en.wikipedia.org/wiki/Bounded_quantification#F-bounded_quantification,其中“F”在本例中仅表示“函数”。你正在跳跃(在 TS 中我们称之为约束 https://www.typescriptlang.org/docs/handbook/generics.html#generic-constraints) 方式T
通过作为它的函数的另一种类型,F<T>
。它也被称为“递归边界” https://stackoverflow.com/questions/7385949/what-does-recursive-type-bound-in-generics-mean.
注意多态性this
是 F 有界多态性的隐式排序,其中this
可以看作是一种“影子”泛型类型参数。你的控制权较少this
与使用显式类型参数相比T
,这使您能够选择子类型触底的位置:
class A implements Cloneable<A> {
/* ... */
clone() {
return new A(this.a.slice());
}
}
class B extends A {
z: string = "oopsie";
}
declare const b: B;
b.clone().z.toUpperCase() // error, no z
Here, b.clone()
产生一个类型的值A
并不是B
, 因为A implements Clonable<A>
并不是Clonable<this>
.
但这里的缺点是你需要在你想要引用的任何地方携带这个“额外”类型参数Cloneable
:
const arrayOfCloneables: Cloneable<A>[] = [new A([1, 2, 3])];
Playground 代码链接 https://www.typescriptlang.org/play?ts=4.2.2#code/HYQwtgpgzgDiDGEAEAFA9gGwJ5jQJxgAsBLeAFRKiQG8BYAKCSaWOABcI8AzBZAYQxpgEEACMMyOo2Yz4g4QAoAlAC4kbSgG4GMgL4MdzVh268kAMTRokEAB4dgAEyoChI8ZMMymokHjVQbHisAOba0kz6EUiOEHJ+yPBCgUhcVmqWaOGyyWxIaBqcmUgAvKlWAHRybsrZzAWERZW+eBVsaACqMDCcfCBQELUG0fFQVACCLGAwEpDsLvLuEjRe3iBqwACuYKKcANoAunXeSEnAgXib8O14CutIWzv7B0or0SdMGsRQFSClSCBjt4oh9TotlG9QTI8BA2Js8MAHhAAO5IcYKL4-EAVKAYUiDJSvfrqLSrPSrKKrUZUABCNnsECcE0hHwAXgEgqF-gAiKwwKDECDcoEg5ixeIw065JCiNQ0oGiKrgpQVVltTrdXr9AnDGTGTg8RBIABKEAAjptiDCoK5hGIJAAeMj0hzOJC2pYQAB8LJOAHo-RUgxTdcwzik-HgQFgAPJcD326BqBMeQ7-PbCVHovYARgANEgAEwFgDMLyODEp9FAkFgZnMNLQmycEEcvpY7ANZhTjuddldCzciadXp9UlB1UUqiQZBFoaY+tMRuK-cZbp7EAdmTHZOYLQ5wWAYRD0XFGASUvOeTSaAyViB4byDSa1jKN6VNSUQOfeEyFRa6pdD0eB9AMQz0FS55jGiUwzBAcxsIOdoeA64w7u8zD3I8ux4IcQI5FelzXPgdwbNsOGHK845Qp8lC-P8gK7pETFgp+7agjCcIIkiWYYnR2K4viyhfixorMSMUG0i6a7MtRJzskgFxcmUvJoPygrCieMhnhej4ynKCoflOqqAZqIHahCAY2HgeD4AWwDWKy84diYhrIKaFpWtAG5OtJTLuosw5kKO7HMAGQYVCeVLSpG0ZxhuUDJoFKFoWmZQZiiaIKLmBbFkgZZKBWEHRJWQA