Typescript:抽象 class 和模块级可见性
Typescript: abstract class and module-level visibility
我看到在Typescript中可以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());
}
}
您可以通过导出一个声明 public 方法的接口来做到这一点,而无需导出 class 本身。
然后,您将需要一个由模块导出的工厂函数,以便能够实例化 class,例如:
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
}
编辑
在不了解您想要做得更好的情况下,您似乎有几个选择,但其中 none 个非常漂亮:
(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);
我看到在Typescript中可以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());
}
}
您可以通过导出一个声明 public 方法的接口来做到这一点,而无需导出 class 本身。
然后,您将需要一个由模块导出的工厂函数,以便能够实例化 class,例如:
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
}
编辑
在不了解您想要做得更好的情况下,您似乎有几个选择,但其中 none 个非常漂亮:
(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);