如何在模块中将构造函数设为私有,但仍允许外部构造

问题描述 投票:0回答:3

我想使模块中的类只能通过给定模块中的辅助函数来构造,这样该类的(模块外部)用户就无法在没有辅助函数的情况下构造它。

简短用法示例:

//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)
}

我有点困惑,没有这样的模块内部访问的访问修饰符(或者我还没有找到它)。关于如何解决这个问题还有其他建议吗?

typescript
3个回答
0
投票

我之前使用过的一种模式是将

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]")

0
投票

我要做的只是避免导出 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'.

0
投票

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中提到的,这也将防止你的类被扩展到模块之外。这可能是理想的,也可能不是理想的,尽管我猜测在许多“内部访问”场景中这就是您想要的。

© www.soinside.com 2019 - 2024. All rights reserved.