Typescript:抽象类和模块级可见性

时间:2017-02-21 09:08:24

标签: typescript encapsulation

我在打字稿中看到你可以emulate module visibility with interfaces,但我不知道在以下情况下是否有可能实现它:

abstract class ConnectionTarget
{
    // callback that subclasses must implement
    protected abstract onConnection: (conn: Connection) => void;

    // property that must be available to subclasses
    protected get connections(): Readonly<Iterable<Connection>>
    {
        return this.conns;
    }

    // private field needed for previous property
    private conns: Connection[] = [];

    // method that SHOULD HAVE MODULE VISIBILITY
    // my module should be able to add connections,
    // but my users shouldn't
    private addConnection(conn: Connection)
    {
        this.conns.push(conn);
        this.onConnection(conn);
    }
}

// my function that needs access to the private members
// the parameter is a user-provided subclass of ConnectionTarget
function doMagicThings(target: ConnectionTarget, source: ConnectionSource)
{
    // do magic tricks here ...

    // method that should be module-protected, like addConnection
    let aConnection: source.createConnection();

    target.addConnection(aConnection);
}

我希望我的用户扩展ConnectionTarget,必须实施onConnection,并且只能使用属性connections,并将其他所有内容隐藏起来。

编辑:示例用法

// class in user code
class MyConnectionTarget extends ConnectionTarget
{
    // users must implement this abstract method
    onConnection(conn: Connection)
    {
        // user specific code here
        // ...

        // can use property 'connections'
        console.log(this.connections)

        // should error here:
        // should not allow to use the following method
        this.addConnection(new Connection());
    }
}

1 个答案:

答案 0 :(得分:1)

您可以通过导出声明公共方法的接口而不导出类本身来实现 然后,您将需要一个工厂函数,该函数由模块导出,以便能够实例化类,如:

export interface IConnectionTarget {
    // public methods will be declared here, i.e:
    myMethod(): void;
}

abstract class ConnectionTarget implements IConnectionTarget {
    private conns: Connection[] = [];

    protected abstract onConnection: (conn: Connection) => void;

    protected get connections(): Readonly<Iterable<Connection>> {
        return this.conns;
    }

    public addConnection(conn: Connection) {
        this.conns.push(conn);
        this.onConnection(conn);
    }

    public myMethod() {}
}

export function createConnectionTarget(): IConnectionTarget {
    // create an instance here and return it
}

code in playground

修改

如果不了解你想要做得更好的事情,似乎你有几个选择,但它们都不是很漂亮:

(1)保持方法私有,并在尝试访问它时转换为any

let aConnection: source.createConnection();
(target as any).addConnection(aConnection);

(2)将ctor中的setter保存到模块级存储:

type Adder = (conn: Connection) => void;
const CONNECTION_ADDERS = new Map<ConnectionTarget, Adder>();

abstract class ConnectionTarget {
    protected constructor() {
        CONNECTION_ADDERS.set(this, this.addConnection.bind(this));
    }

    private addConnection(conn: Connection) { ... }
}

然后使用它:

let aConnection: source.createConnection();
CONNECTION_ADDERS.get(aConnection)(aConnection);