我想使模块中的类只能通过给定模块中的辅助函数来构造,这样该类的(模块外部)用户就无法在没有辅助函数的情况下构造它。
简短用法示例:
//module: email.ts
export class Email {
//implementation details
}
export function tryParse(x: string): Email | null {
//implementation details
}
然后我想像这样使用它:
//module: email_usage.ts
import {Email, tryParse} from "./email.ts"
//works
let x?: Email = tryParse("[email protected]")
//but this should fail with an compiler error
let y: Email = new Email("foo")
我对上述问题有一个解决方案......但你感觉很奇怪
export class Email {
//I need to make the constructor protected here so it cant be accessed outside
protected constructor(x: string) {
this._value = x
}
private _value: string
get Value () {
return this._value
}
}
//then I create a "Factory" class that extends from Mail so I have access to the protected constructor
class EmailFactory extends Email {
// and then I create a "public" static method to finally create an Email instance
static create(x: string): Email {
return new Email(x)
}
}
export function tryParse(x: string): Email | null {
return EmailFactory.create(x)
}
我有点困惑,没有这样的模块内部访问的访问修饰符(或者我还没有找到它)。关于如何解决这个问题还有其他建议吗?
我之前使用过的一种模式是将
tryParse
帮助器作为静态方法移动到 Email
类本身中,并且由于它位于类内部,因此它可以访问私有构造函数:
export class Email {
static tryParse(x: string): Email | null {
return new Email(x)
}
private constructor(x: string) {
this._value = x
}
private _value: string
get Value () {
return this._value
}
}
// Then you can construct the email like so:
let x = Email.tryParse("[email protected]")
我要做的只是避免导出 Email 类,而只从模块中导出 tryParse 函数。您可以将其包含在类似于您所做的工厂类中:
// module A
class Email {
private _value: string;
constructor(x: string) {
this._value = x;
}
get Value() {
return this._value;
}
}
export class EmailFactory extends Email {
public static parse(x: string): Email {
return isValidEmail(x) ? new Email(x) : null;
}
}
// module B
import {EmailFactory} from "ModuleA";
EmailFactory.parse("foo");
// => null
EmailFactory.parse("[email protected]");
// => Email
new Email("[email protected]");
// error TS2304: Cannot find name 'Email'.
Typescript 3.8 引入了“导出类型”语法,可以在这里使用。
在您的模块中,照常导出辅助函数,但通过
export type
而不是 export
导出类:
// Don't export `Email` here:
class Email { ... }
export function tryParse(x: string): Email | null { ... }
// Instead, export `Email` here, but only as a `type`:
export type { Email };
这将阻止从模块外部构造该类,但是
tryParse
的调用者仍然会收到一个可以正常使用的类型化 Email
对象。正如docs中提到的,这也将防止你的类被扩展到模块之外。这可能是理想的,也可能不是理想的,尽管我猜测在许多“内部访问”场景中这就是您想要的。