TypeScript class 使用私有函数实现 class

TypeScript class implements class with private functions

我正在探索 class 在 TypeScript 中实现 class 的可能性。

于是,我写了下面的代码Playground link

class A {
    private f() { console.log("f"); }
    public g() { console.log("G"); }
}

class B implements A {
    public g() { console.log("g"); }
}

我得到了错误:Class 'B' incorrectly implements class 'A' --- property 'f' is missing in type 'B' 加上一个建议,我的意思是 extends

所以我尝试创建一个名为 f 的私有字段(public 不起作用,因为它检测到它们具有不同的访问修饰符)Playground link

现在我得到错误:Class 'B' incorrectly implements class 'A'. Types have separate declarations of a private property 'f';这让我很困惑:

我不会在实践中这样做,但我很好奇为什么 TS 会这样工作。

谢谢!

在这种情况下,当前的打字稿规范是不可能的。有一个 tracked issue 用于此,但它已关闭。

Suggestion: Permit an implementing class to ignore private methods of the implementee class.


另请参阅 在 TypeScript 中扩展与实现纯抽象 class

问题 Microsoft/TypeScript#18499 讨论了为什么在确定兼容性时需要私有成员。原因是:class 私有成员 可见 相同 class.

的其他实例

@RyanCavanaugh 的一个 remark 特别相关和有启发性:

Allowing the private fields to be missing would be an enormous problem, not some trivial soundness issue. Consider this code:

class Identity { private id: string = "secret agent"; public sameAs(other: Identity) { return this.id.toLowerCase() === other.id.toLowerCase(); } } class MockIdentity implements Identity { public sameAs(other: Identity) { return false; } }
MockIdentity is a public-compatible version of Identity but attempting to use it as one will crash in sameAs when a non-mocked copy interacts with a mocked copy.

明确一点,这是它会失败的地方:

const identity = new Identity();
const mockIdentity = new MockIdentity();
identity.sameAs(mockIdentity); // boom!

因此,您有充分的理由不能这样做。


作为一种变通方法,您可以只提取具有如下映射类型的 class 的 public 属性:

type PublicPart<T> = {[K in keyof T]: T[K]}

然后你可以 B 实施而不是 APublicPart<A>:

class A {
    private f() { console.log("f"); }
    public g() { console.log("G"); }
}

// works    
class B implements PublicPart<A> {
    public g() { console.log("g"); }
}

希望对您有所帮助;祝你好运!

这主要是因为私有成员的可见性仅限于类型,而不是实例。这意味着类型 T 的所有对象都可以访问类型 T 的其他对象的私有。

这在主格类型语言中不是问题,因为 T 的所有实例都继承了 T 的实现,但由于打字稿是结构类型的,这意味着我们不能假设所有满足 T 的实例都具有 T 的实现class 声明类型 T。

这意味着私有范围的成员必须是类型的 public 契约的一部分,否则结构类型 T 的对象可以调用另一个对象的 non-existing 私有成员相同的结构类型。

被迫让私人成为 public 类型契约的一部分是不好的,并且可以通过将私人范围限定在实例而不是类型来避免。

当前具有 Typescript 开箱即用支持的解决方案很简单

class A {
    private f() { console.log("f"); }
    public g() { console.log("G"); }
}

class B implements Pick<A, keyof A> {
    public g() { console.log("g"); }
}

解释:keyof A只有returns public A的属性(和方法),然后Pick会向下trimA 仅显示其 public 属性及其各自的类型。