首页 > 解决方案 > 如何在 TypeScript 中模拟私有和公共构造函数重载

问题描述

我有一个旨在从两个不同的地方构建的类,即类外和类内。

定义类的模块提供了一个对象,该类在其闭包环境中捕获该对象。

从类内部(例如方法调用),类是用对象构造的。

下面是它在 JavaScript 中的样子:

const private_accessor = ...;

export class T {
    create() {
        return new T(private_accessor);
    }

    constructor(key) {
        if (key === private_accessor) {
            // trusted call
        } else {
            // outside callee
        }
    }
}

我们可以将构造函数键入为constructor(key?: object),但实际上,这并不能反映该类的使用方式。

我认为直观的答案是:

const private_accessor: object_type = ...;

export class T {
    create(): T {
        return new T(private_accessor);
    }

    private constructor(key: object_type);
    public constructor();
    constructor(key /* object_type? */) {
        if (key === private_accessor) {
            // trusted call
        } else {
            // outside callee
        }
    }
}

由于私有重载是一个实现细节,并且无论如何都不会在外部被调用者中工作,因此没有理由公开类型重载。

但是TSC不允许这样做。

还有另一种方法可以实现我想要做的事情吗?

标签: typescriptprivate

解决方案


虽然拥有多个具有不同可见性的调用/构造签名会很好,但这只是语言不支持的。请参阅作为设计限制关闭的microsoft/TypeScript#9592:它似乎不是一个足够常见的用例来保证额外的努力和编译器的复杂性。


相反,我建议您考虑拥有一组单独的面向外部和面向内部的接口,其中外部接口仅包含“公共”功能,而内部接口还包含“私有”功能。当您export的值/类型时,仅公开公共功能:

class T {
  create(): T {
    return new T(private_accessor);
  }

  constructor(key?: object_type) {
    if (key === private_accessor) {
      // trusted call
    } else {
      // outside callee
    }
  }
}

// public version of T 
const Tpublic: new () => T = T;

export { Tpublic as T }; // renamed

在这里,类的内部版本T可以两种方式构造,但是您将其导出为仅具有无参数构造签名的东西。因此,导入此库的其他代码无法使用“私有”构造函数:

import { T } from "./lib";

const t = new T(); // okay
t.create(); // okay

new T("something"); // error!
//    ~~~~~~~~~~~ Expected 0 arguments, but got 1.ts

CodeSandbox 代码链接


推荐阅读