缓存构造函数导致"属性' ...'没有初始值设定项,并且在构造函数中没有明确赋值。"

时间:2018-02-03 20:55:37

标签: typescript constructor

我的项目中时不时地创建带有构造函数的类,这些构造函数缓存它们创建的对象,这样如果构造函数被多次调用相同的参数,它每次都会返回相同的实例,而不是创建一个新的实例。与已创建的相同。

这是一个最小的例子:

class X {
    private static __cache: Record<string, X> = Object.create(null);

    readonly name: string; // The compilation error happens on this line.

    constructor(name: string) {
        const cached = X.__cache[name];
        if (cached !== undefined) {
            return cached;
        }

        this.name = name;
        X.__cache[name] = this;
    }
}

此代码适用于TypeScript,直到我移至2.7并打开strictPropertyInitialization。现在我在readonly name: string;

时收到错误
  

财产&#39;名称&#39;没有初始值设定项,并且在构造函数中没有明确赋值。

我的项目中有多个具有上述模式的类,所以我需要提出一个或多个通用解决方案来消除错误。

我不想要的两个解决方案:

  1. 关闭strictPropertyInitialization。我发现将其关闭通常太有用了。打开它显示了一些需要更新的定义,以更好地反映我的某些类的工作方式,或者提示初始化代码的改进。

  2. name添加明确的赋值断言,使其声明为readonly name!: string;。感叹号导致TypeScript不再检查name是否已明确分配。这消除了错误,但它也在编译器中打出太大的漏洞检查我的品味。例如,如果我使用断言并且我不小心在上面的代码中删除了作业this.name = name,那么TypeScript不会引发错误。我希望尽早收到错误通知。

  3. 我在上面提供了一个最小的例子,但是在我的应用程序中,我有更多字段的类,或者是从非常昂贵的计算中创建的字段,而不仅仅是从构造函数参数中分配的。

3 个答案:

答案 0 :(得分:1)

对于对象的字段计算成本高昂的情况,我到目前为止所采用的解决方案是将constructor标记为privateprotected是或许在某些情况下表示)并将工厂函数声明为类的静态成员。像这样:

class X2 {
    private static __cache: Record<string, X2> = Object.create(null);

    readonly name: string;

    private constructor(nameSource: string) {
        this.name = expensiveComputation(nameSource);
    }

    // We use this factory function to create new objects instead of
    // using `new X2` directly.
    static make(name: string): X2 {
        const cached = X2.__cache[name];
        if (cached !== undefined) {
            return cached;
        }

        return X2.__cache[name] = new X2(name);
    }    
}

因为构造函数总是设置其所有字段,所以TypeScript不再存在问题。这要求使用类的代码使用工厂函数来创建新对象,而不是直接使用构造函数。

答案 1 :(得分:0)

我使用的另一种解决方案是我的构造函数只将其参数分配给字段而不执行任何重要计算的情况是在检查是否存在之前,只需翻转构造函数中的逻辑以执行字段分配。已创建的实例。如果事实证明某个实例已经存在,那么字段分配可能会不必要地完成,但这不是我要担心的事情,直到分析显示它是真实应用程序中的真正问题。

看起来像这样:

class X {
    private static __cache: Record<string, X> = Object.create(null);

    readonly name: string;

    constructor(name: string) {
        // Set the fields first...
        this.name = name;

        // And then figure out whether we have an instance to return.
        const cached = X.__cache[name];
        if (cached !== undefined) {
            return cached;
        }

        X.__cache[name] = this;
    }
}

使用参数属性,它甚至可以简化为:

class X {
    private static __cache: Record<string, X> = Object.create(null);

    // The parameter on the constructor also defines the property on 
    // instances of X.
    constructor(readonly name: string) {

        // And then figure out whether we have an instance to return.
        const cached = X.__cache[name];
        if (cached !== undefined) {
            return cached;
        }

        X.__cache[name] = this;
    }
}

答案 2 :(得分:-1)

https://www.ryadel.com/en/ts2564-ts-property-has-no-initializer-typescript-error-fix-visual-studio-2017-vs2017

  

如果问题发生在我们自己的代码中,那么我们可以做的最好的事情就是通过添加确定的赋值声明修饰符(如上文所述)来手动修复该问题:在大多数情况下,很多!放置在正确的位置就足以修复您的项目。