Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 导出类不像使用私有构造函数那样有限制吗?_Typescript_Ecmascript 6 - Fatal编程技术网

Typescript 导出类不像使用私有构造函数那样有限制吗?

Typescript 导出类不像使用私有构造函数那样有限制吗?,typescript,ecmascript-6,Typescript,Ecmascript 6,对于我正在编写的API,我希望确保传递的值只能由我自己的特定函数实例化,以确保所述值的有效性。我知道,经过验证的方法是使用私有构造函数和静态工厂方法创建一个类,如下面的示例(假设的情况;实际的类更复杂,并且有方法) 但是为了允许更功能化的方法,我希望工厂函数位于类本身之外(但在同一个模块中),以便能够更自由地组合不同的变体。我提出了以下方法,将构造函数保持为公共的,但通过不导出将其限制为同一模块中的函数: class ServiceEndpoint { constructor(

对于我正在编写的API,我希望确保传递的值只能由我自己的特定函数实例化,以确保所述值的有效性。我知道,经过验证的方法是使用私有构造函数和静态工厂方法创建一个类,如下面的示例(假设的情况;实际的类更复杂,并且有方法)

但是为了允许更功能化的方法,我希望工厂函数位于类本身之外(但在同一个模块中),以便能够更自由地组合不同的变体。我提出了以下方法,将构造函数保持为公共的,但通过不导出将其限制为同一模块中的函数:

class ServiceEndpoint {
    constructor(
        readonly name: string,
        readonly address: string,
        readonly port: number
    ) {}
}

export function getEndpointByName(name: string): ServiceEndpoint {
    // ... polling operation that results in an address
    return new ServiceEndpoint(name, address, port);
}

测试这个似乎会产生相同的结果,但我没有看到其他人这样做,所以我有点谨慎。我是否正确地假设这会阻止模块用户自己实例化类,就像私有构造函数那样?我正在监督的这种方法有缺点吗?

我对你的问题很感兴趣,我做了一些测试,试图以最好的方式回答这个问题

首先,请记住TypeScript不是JavaScript,它最终将在运行之前进行编译。在声明构造函数之前编写
private
不会对编译后的代码产生特别的影响,换句话说,这样做不会增加一点“安全性”

正如@Bergi在注释中正确指出的那样,通常您可以通过实例
构造函数
属性访问构造函数,并可能执行
const illagalinstance=new-laginstance.constructor()

通过完全删除
构造函数
引用,可以避免最后一种情况。比如:

class-MyClass{
构造函数(){}
}
删除MyClass.prototype.constructor;
导出函数myFactory(){
返回新的MyClass();
}
为了更具体地解决您的问题,在删除
构造函数
引用之后,不导出类就足以假定在该模块之外不会创建非法实例。(然而,我决不会依赖内存中的任何内容来实现安全关键场景)

最后,您可以在构造函数中执行一些检查,如果这些检查不通过,则抛出错误。这将防止类被实例化

我没见过其他人这样做

请记住,
class
语法只是构造函数的语法糖。最后,唯一重要的是结果对象及其原型的最终结果


啊!!不要担心
导出类型{ServiceEndpoint},同样,这不是JavaScript,将在编译时删除。

我可以想到的一种方法是在实例化之前检查模块明智的/private
secret
,如下所示

// module -- ServiceEndpoint.ts   
const secret = Symbol('secret'); // do not export it so that it can be kept "private"

class ServiceEndpoint {
    // similar to class decorator ...
    static _secret: Symbol | undefined;
    constructor(
        readonly name: string,
        readonly address: string,
        readonly port: number
    ) {
        if(ServiceEndpoint._secret !== secret) {
            throw 'you can only instantiate this class using factory method'
        } else {
          // normal instantiation ....
        }
    }
}

export type { ServiceEndpoint} 

export function getEndpointByName(name: string): ServiceEndpoint {
    ServiceEndpoint._secret = secret;
    // ... polling operation that results in an address
    const instance =  new ServiceEndpoint(name, '', 0);
    ServiceEndpoint._secret = undefined;
    return instance;
}

// -----------------------------------------------------------------

// run example.ts
// outside the above module, you can call
getEndpointByName('haha');
// but the following will fail, i.e. throw exception 
new ServiceEndpoint('', '', 1);

另外,如果您使用此实例的构造函数,它也会失败。

在ES6中,您可以始终执行
const someEndpoint=getEndPointByName(…);const myEndpoint=new(someEndpoint.constructor)(…)。但是,如果您将构造函数设置为私有的,我想TypeScript会抱怨。在后一种情况下,类
ServiceEndpoint
不会暴露在此模块之外,这就是您想要的吗?我希望
ServiceEndpoint
只能作为返回值访问。在这两种情况下,我都可以
导出类型{ServiceEndpoint}
要访问声明中使用的类型(我假设:这可能是gotcha的一部分),您不应该通过将机密存储为类上的静态属性来进行通信。最坏的情况,就是这样泄露的。相反,将该秘密作为一个附加构造函数参数传递。我可以通过执行
Object.defineProperty(ServiceEndpoint,“\u secret”,{set(value){console.log(secret,“谢谢!”;}})访问该秘密;getEndpointByName('haha')(或其变体)。它在构造函数签名中是否可见并不重要,因为构造函数本身并不公开可见。如果您坚持使用标志进行通信(这是一种脆弱的方法,在任何情况下都不能很好地处理可重入性、回调或异常),至少不要将其作为公共静态属性!您有一个非常好的模块范围。好吧,但即使您不关心安全性,它仍然是一个过于复杂和脆弱的方法。使用一个简单的
let callFromFactory=false
boolean标志。如果
let callFromFactory
是私有的,谁会关心
false
是否为“私有”呢?它将被定义在与您的
const secret
相同的范围内。另请参阅,以了解仍然提供用于
instanceof
的构造函数的类似方法
// module -- ServiceEndpoint.ts   
const secret = Symbol('secret'); // do not export it so that it can be kept "private"

class ServiceEndpoint {
    // similar to class decorator ...
    static _secret: Symbol | undefined;
    constructor(
        readonly name: string,
        readonly address: string,
        readonly port: number
    ) {
        if(ServiceEndpoint._secret !== secret) {
            throw 'you can only instantiate this class using factory method'
        } else {
          // normal instantiation ....
        }
    }
}

export type { ServiceEndpoint} 

export function getEndpointByName(name: string): ServiceEndpoint {
    ServiceEndpoint._secret = secret;
    // ... polling operation that results in an address
    const instance =  new ServiceEndpoint(name, '', 0);
    ServiceEndpoint._secret = undefined;
    return instance;
}

// -----------------------------------------------------------------

// run example.ts
// outside the above module, you can call
getEndpointByName('haha');
// but the following will fail, i.e. throw exception 
new ServiceEndpoint('', '', 1);