【发布时间】:2021-03-06 04:42:55
【问题描述】:
考虑一个 typescript 接口,它描述了一个对象有一个 .clone() 方法的约束,该方法返回一个相同类型的对象(或者可能是一个可分配给它自己类型的对象)。我想象的一个例子:
// Naive definition
interface Cloneable {
clone: () => Clonable;
}
class A implements Cloneable {
a: number[];
constructor(a: number[]) {
this.a = a;
}
clone(): A {
return new A(this.a.slice()); // returns a deep copy of itself
}
}
interface RequiresCloneable<T extends Cloneable> {
//...
}
const arrayOfCloneables: Clonable[] = [new A([1,2,3])];
虽然这可行,但该接口未能捕捉到我们对 Cloneable 应如何表现的理解:它承认一个类 Faker,其 .clone() 方法返回某个其他类的可克隆对象,该对象无法分配给类型 Faker。为了解决这个问题,我们可以按类型参数化 Cloneable 接口:
// Approach 1
interface Cloneable<T> {
clone: () => T;
}
// Approach 2
interface Cloneable<T> {
clone: () => Cloneable<T>;
}
// Approach 3. This pattern looks quite strange but this seems to work the best.
interface Cloneable<T extends Cloneable<T>> {
clone: () => T;
}
然后将类型参数约束为可克隆,我们可以指定T extends Cloneable<T>:
// Interface that requires a cloneable
interface RequiresCloneable<T extends Cloneable<T>> {
//...
}
所有三个参数化接口都承认类A。但是,我们失去了在不知道其实现类型的情况下指定特定值应该是 Cloneable 的能力:
const clonableA: Cloneable<A> = new A([1]);
const clonableOfUnknownType: Cloneable<unknown>; // admits Fakers with approach 1 and 2, doesn't compile with approach 3
我试图围绕类型系统的工作原理以及在类型系统下什么是可能的和不可能的。我的问题是:
- 是否有任何方法可以制作非泛型 Cloneable 接口或类型来捕捉对象的克隆应该与自身相同的类型?如果不是,那么 typescript 类型系统的哪些属性会导致这种情况?
- 在我上面描述的三个通用
Cloneable<T>接口中,哪一个是最好的? Best 可能意味着最清晰、最简单、最简洁、最惯用或最严格。似乎第三个是唯一不承认伪造者的界面。有没有更好的方法来做他们正在做的事情? - 在 typescript 中,或更一般地在任何类型系统中是否有模式
interface S<T extends S<T>>的名称?
【问题讨论】:
标签: typescript