ChatGPT解决这个技术问题 Extra ChatGPT

属性“...”没有初始值设定项,也没有在构造函数中明确分配

在我的 Angular 应用程序中,我有一个组件:

import { MakeService } from './../../services/make.service';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-vehicle-form',
  templateUrl: './vehicle-form.component.html',
  styleUrls: ['./vehicle-form.component.css']
})
export class VehicleFormComponent implements OnInit {
  makes: any[];
  vehicle = {};

  constructor(private makeService: MakeService) { }

  ngOnInit() {
    this.makeService.getMakes().subscribe(makes => { this.makes = makes
      console.log("MAKES", this.makes);
    });
  }

  onMakeChange(){
    console.log("VEHICLE", this.vehicle);
  }
}

但在“制造”属性中我有一个错误。我不知道该怎么办...

https://i.stack.imgur.com/QpJLJ.png


M
Martin Čuka

只需转到 tsconfig.json 并设置

"strictPropertyInitialization": false

摆脱编译错误。

否则,您需要初始化所有变量,这有点烦人


只需确保在 "strict": true 之后添加它,否则转译器似乎再次将其打开(尽管 VS 似乎知道它已关闭)。
这样,您将禁用所有项目的严格检查属性初始化。最好将 ! 后缀运算符添加到变量名称中,以忽略这种情况,或者在构造函数中初始化变量。
您建议忽略编译器所说的潜在问题,这并不安全。所以投反对票。
StrictPropertyInitialzer 是 typescript 2.7 中引入的标志。每个人都可以选择是否要启用所有这些标志并严格遵守所有规则或关闭某些验证。每次都遵守所有规则是完全没有意义的。如果您的代码变得过于冗长和缺点并且大于优点,您应该明确地将其关闭。我并不是说这在所有情况下都是最佳实践,但在大多数情况下它绝对是可行的选择......
正如我在回答中提到的,您有两个选项,您可以禁用验证或初始化所有变量。每个人都可以选择您将从项目中受益更多的项目。
C
Clashsoft

我认为您使用的是最新版本的 TypeScript。请参阅 link 中的“严格类初始化”部分。

有两种方法可以解决此问题:

A. 如果您使用的是 VSCode,您需要更改编辑器使用的 TS 版本。

B. 声明时初始化数组

makes: any[] = [];

或在构造函数内部:

constructor(private makeService: MakeService) { 
   // Initialization inside the constructor
   this.makes = [];
}

正如错误所说,您需要将变量初始化为某个值:any[] = [];
您必须使用“明确赋值断言”来告诉 typescript 此变量在运行时将具有如下值:makes!: any[];
嗨@Sajeetharan,如何更改 vscode ts 版本
在构造函数中初始化变量是 Angular 的反模式。不要这样做。
@KumaresanSd ctrl+shift+p 并在下拉列表中输入 typescript,然后选择“选择 TypeScript 版本”
a
anothernode

这是因为 TypeScript 2.7 包含一个严格的类检查,所有属性都应该在构造函数中初始化。一种解决方法是将 ! 作为后缀添加到变量名称:

makes!: any[];

这 ”!”对于那些无法保证立即定义值的常见情况,存在语法。这是一个逃生舱,不应该依赖它,因为它会使您的代码不那么安全。默认值通常是首选。不过很高兴知道它的存在
@kingdaro 是对的。虽然这通常可以使用,但它也可能导致 flat 不起作用的代码。例如,在 VS2017 生成的基本 webapp 中,通过添加“!”更改 fetchdata.components.ts 中预测的分配(公共预测!:WeatherForecast[];)这将导致它完全出错
这是最好的解决方案,因为它直接在 @Input() 装饰器之后(在新的角度),当读取任何给定的组件时,它会自然地读取并剔除任何开发错误。
顺便说一句,与使用有什么区别!或者 ?处理空值或未定义?
我需要在应用程序的服务中定义一个使用枚举的属性,我不能假设用户的选择,甚至不是默认值。它是在第一步中定义的,直到第二步才被读取,所以这是非常有用的转义,很高兴知道它的存在,非常感谢。
S
Stephane

tsconfig.json 文件中添加一些配置时,我们可能会收到消息 Property has no initializer and is not definitely assigned in the constructor,以便在严格模式下编译 Angular 项目:

"compilerOptions": {
  "strict": true,
  "noImplicitAny": true,
  "noImplicitThis": true,
  "alwaysStrict": true,
  "strictNullChecks": true,
  "strictFunctionTypes": true,
  "strictPropertyInitialization": true,

实际上,编译器随后会抱怨在使用之前未定义成员变量。

对于在编译时未定义的成员变量的示例,具有 @Input 指令的成员变量:

@Input() userId: string;

我们可以通过声明变量可能是可选的来使编译器静音:

@Input() userId?: string;

但是,我们将不得不处理未定义变量的情况,并用一些这样的语句使源代码混乱:

if (this.userId) {
} else {
}

相反,知道这个成员变量的值会及时定义,也就是说,它会在使用之前定义,我们可以告诉编译器不要担心它没有被定义。

告诉编译器的方法是添加 ! definite assignment assertion 运算符,如下所示:

@Input() userId!: string;

现在,编译器明白这个变量虽然没有在编译时定义,但应该在运行时定义,并且在使用它之前及时定义。

现在由应用程序确保在使用之前定义此变量。

作为一个额外的保护,我们可以在使用它之前断言正在定义的变量。

我们可以断言变量已定义,即所需的输入绑定实际上是由调用上下文提供的:

private assertInputsProvided(): void {
  if (!this.userId) {
    throw (new Error("The required input [userId] was not provided"));
  }
}

public ngOnInit(): void {
  // Ensure the input bindings are actually provided at run-time
  this.assertInputsProvided();
}

知道变量已定义,现在可以使用该变量:

ngOnChanges() {
  this.userService.get(this.userId)
    .subscribe(user => {
      this.update(user.confirmedEmail);
    });
}

请注意,在输入绑定尝试之后调用 ngOnInit 方法,即使没有向绑定提供实际输入也是如此。

ngOnChanges 方法是在输入绑定尝试之后调用的,并且只有在绑定提供了实际输入时才调用。


这是使用“严格”模式时的正确答案
除了一件事之外,这对我来说很多都是有意义的……断言会给你什么?可以进行调试,但不适用于使用该应用程序。抛出不会被捕获的错误不是一个好主意。您应该提供一些后备措施,无论如何都会使事情正常进行。
这应该被认为是正确的答案,因为它解决了问题,而不仅仅是转动部分棉绒。
在严格模式下使用“确定赋值断言”只是为了避免条件检查并没有多大意义。您正在破坏拥有严格类型系统的整个逻辑。如果避免这些检查对您来说更重要,那么您可能不应该使用严格模式。
c
crispengari

转到您的 tsconfig.json 文件并更改属性:

 "noImplicitReturns": false

然后添加

 "strictPropertyInitialization": false

"compilerOptions" 属性下。

您的 tsconfig.json 文件应如下所示:


{
      ...
      "compilerOptions": {
            ....
            "noImplicitReturns": false,
            ....
            "strictPropertyInitialization": false
      },
      "angularCompilerOptions": {
         ......
      }  
 }

希望这会有所帮助!

祝你好运


IMO,它有点倒退。
首先,感谢您的解决方案,它有效。其次,谁能解释一下刚刚发生的事情?
它起作用了,在以角度创建指令时是必要的。
A
Alexandre Annic

该错误是合法,可能会阻止您的应用崩溃。您将 makes 键入为一个数组,但它也可以是未定义的。

您有 2 个选项(而不是禁用打字稿存在的原因......):

1. 在您的情况下,最好将 makes 键入为可能未定义。

makes?: any[]
// or
makes: any[] | undefined

因此,每当您尝试访问 makes 时,编译器都会通知您它可能未定义。否则,如果在 getMakes 完成之前执行了下面的 // <-- Not ok 行,或者如果 getMakes 失败,您的应用程序将崩溃并引发运行时错误。这绝对不是你想要的。

makes[0] // <-- Not ok
makes.map(...) // <-- Not ok

if (makes) makes[0] // <-- Ok
makes?.[0] // <-- Ok
(makes ?? []).map(...) // <-- Ok

2. 你可以假设它永远不会失败,并且在初始化之前你永远不会尝试通过编写下面的代码来访问它(有风险!)。所以编译器不会关心它。

makes!: any[] 

进一步来说,

您的代码设计可能会更好。定义局部和可变变量不是一个好习惯。您应该管理服务内的数据存储:

首先是零安全的,

其次,能够分解大量代码(包括打字、加载状态和错误)

最后是为了避免过多和无用的reftech。

下面的例子试图展示这一点,但我没有测试它,它可以改进:

type Make = any // Type it

class MakeService {

  private readonly source = new BehaviorSubject<Make[] | undefined>(undefined);
  loading = false;

  private readonly getMakes = (): Observable<Make[]> => {
    /* ... your current implementation */
  };

  readonly getMakes2 = () => {
    if (this.source.value) {
      return this.source.asObservable();
    }
    return new Observable(_ => _.next()).pipe(
      tap(_ => {
        this.loading = true;
      }),
      mergeMap(this.getMakes),
      mergeMap(data => {
        this.source.next(data);
        return data;
      }),
      tap(_ => {
        this.loading = false;
      }),
      catchError((err: any) => {
        this.loading = false;
        return throwError(err);
      }),
    );
  };
}

@Component({
  selector: 'app-vehicle-form',
  template: `
    <div *ngIf="makeService.loading">Loading...</div>
    <div *ngFor="let vehicule of vehicules | async">
      {{vehicle.name}}
    </div>
  `,
  styleUrls: ['./vehicle-form.component.css']
})
export class VehicleFormComponent implements OnInit {
  constructor(public makeService: MakeService) {}

  readonly makes = this.makeService.getMakes2().pipe(
    tap(makes => console.log('MAKES', makes))
  );

  readonly vehicules = this.makes.pipe(
    map(make => make/* some transformation */),
    tap(vehicule => console.log('VEHICLE', vehicule)),
  );

  ngOnInit() {
    this.makes.subscribe(makes => {
      console.log('MAKES', makes);
    });
  }
}

这似乎是唯一正确的解决方案。我被其他答案吓坏了。
我也认为这个错误信息很有用。所以在 tsconfig.json 中停用它不是合适的解决方案。并添加“!” can 允许编译器放手,但它使代码不太确定,除非你真的确定发生了什么。一个更好的解决方案似乎是理解该属性可以不定义,因此开发人员必须相应地编写代码。这是我回答的目的。
@Rei Miyasaka,这个“功能”后来被添加到打字稿中,这是否意味着之前编写的所有代码都是错误的?不,不是的。当您拥有 hello world 应用程序时,请随意遵循所有规则。但是一旦你有一个庞大的项目(这是像打字稿这样的语言开始的原因),那么你应该决定如何编写代码并且所有人都应该遵循它。我认为在 tsconfig.json 中禁用此特定选项没有任何问题。
@MartinČuka 仅仅因为每个人都在这样做并不意味着它没有错或它并不可怕。 TypeScript 本身的出现是因为 JavaScript 有点可怕。项目越大,您越应该严格遵守类型规则。
选项 2 在 : 之前添加 ! 更为常见。
S
Sumit

2021 年更新:

有像“strictPropertyInitialization”这样的属性

只需转到 tsconfig.json 并设置

“严格”:假

摆脱编译错误。

否则,您需要初始化所有变量,这有点烦人。

此错误背后的原因是:

与 javascript 相比,typescript 是一种更安全的语言。

尽管通过启用严格的功能来增强这种安全性。所以每次初始化变量时打字稿都希望它们分配一个值。


可怕的建议,抛弃了类型安全性,以便您看到更少的错误,而不是为了安全而放弃一些便利。
k
kshetline

您要么需要禁用 Sajeetharan 提到的 --strictPropertyInitialization,要么执行以下操作以满足初始化要求:

makes: any[] = [];

B
Bronzdragon

如果您真的不想初始化它,也可以执行以下操作。

makes?: any[];

A
Astrophage

如果要基于接口初始化对象,可以使用以下语句将其初始化为空。

myObj: IMyObject = {} as IMyObject;

M
Md Wahid

在使变量之后放一个问号(?)。

  makes?: any[];
  vehicle = {};

  constructor(private makeService: MakeService) { }

它现在应该可以工作了。我正在使用 angular 12,它适用于我的代码。


c
codejockie

从 TypeScript 2.7.2 开始,如果在声明时未分配属性,则需要在构造函数中初始化该属性。

如果你来自 Vue,你可以尝试以下方法:

添加 "strictPropertyInitialization": true 到您的 tsconfig.json

如果你对禁用它不满意,你也可以试试这个 make: any[] |不明确的。这样做需要您使用空检查 (?.) 运算符访问属性,即 this.makes?.length

你也可以试试 make!:any[];,这告诉 TS 该值将在运行时分配。


x
xgqfrms

有很多解决方案

演示代码

class Plant {
  name: string;
  // ❌ Property 'name' has no initializer and is not definitely assigned in the constructor.ts(2564)
}

解决方案

添加“未定义”类型

class Plant {
  name: string | undefined;
}

添加明确的赋值断言

class Plant {
  name!: string;
}

用初始值声明👍

class Plant {
  name: string = '';
}

使用带有初始值的构造函数👍

class Plant {
  name: string;
  constructor() {
    this.name = '';
  }
}

通过参数使用构造函数和初始化值👍

class Plant {
  name: string;
  constructor(name: string) {
    this.name = name ?? '';
  }
}

5的简写👍

class Plant {
  constructor(public name: string = name ?? '') {
    // 
  }
}

tsconfig.json

不建议

{
  "compilerOptions": {
+   "strictPropertyInitialization": false,
-   "strictPropertyInitialization": true,
  }
}

参考

https://www.typescriptlang.org/docs/handbook/2/classes.html#--strictpropertyinitialization


T
TheMysterious

击球手的方法是将感叹号添加到您确定它不应为 undefined 或 null 的变量的末尾,例如,您使用的 ElementRef 需要从模板加载并且不能在构造函数中定义,执行如下操作

class Component {
 ViewChild('idname') variable! : ElementRef;
}

s
samivic

如果你不想改变你的 tsconfig.json,你可以像这样定义你的类:

class Address{
  street: string = ''
}

或者,您也可以这样进行:

class Address{
  street!: string
}

通过添加感叹号“!”在您的变量名之后,Typescript 将确保该变量不为空或未定义。


P
Pinaki

在我的 Angular 项目中添加节点时出现此错误 -

TS错误:?无法编译 TypeScript:(路径)/base.api.ts:19:13 - 错误 TS2564:属性 'apiRoot Path' 没有初始化程序,并且未在构造函数中明确分配。私有 apiRootPath:字符串;

解决方案 -

tsconfig.json 的 'compilerOptions' 中添加了 "strictPropertyInitialization": false

我的 package.json -

"dependencies": {
    ...
    "@angular/common": "~10.1.3",
    "@types/express": "^4.17.9",
    "express": "^4.17.1",
    ...
}

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


M
M Komaei

在 tsconfig.json 文件中,在“compilerOptions”中添加:

"strictPropertyInitialization": false,

https://i.stack.imgur.com/SUiVW.png


M
Masoud Bimmar

属性“...”没有初始化程序,并且在 Angular 的构造函数错误修复中未明确分配

解决方案 1:禁用 strictPropertyInitialization 标志

在 Angular 应用程序中修复此错误的简单方法是在 tsconfig.json 文件的 typescript 编译器选项中禁用 --strictPropertyInitialization 标志。

"compilerOptions": {
  ///
  ,
  "strictPropertyInitialization":false
}

解决方案 2:向属性添加未定义类型

有一个未定义的属性是可以的。

因此,在声明变量时,将未定义的类型添加到属性中。

employees: Employee[];

//Change to 

employees : Employee[] | undefined;

解决方案 3:向属性添加明确的赋值断言

如果您知道我们将在稍后的时间点分配该物业。

最好为属性添加明确的赋值断言。即员工。

employees!: Employee[];

解决方案 4:将初始化程序添加到属性

消除这种类型错误的另一种方法是向属性添加显式初始化程序。

employees: Employee[] = [];

解决方案 5:构造函数中的赋值

否则,我们可以为构造函数中的属性分配一些默认值。

employees: Employee[];

constructor() { 
    this.employees=[];
}

Best Solutions from here


J
Jai Govind Gupta

当你使用 typescript@2.9.2 升级时,它的编译器严格遵循组件类构造函数中数组类型声明的规则。

要解决此问题,请更改代码中声明的代码或避免编译器在“tsconfig.json”文件中添加属性“strictPropertyInitialization”:false 并再次运行 npm start 。

Angular web 和移动应用程序开发你可以去 www.jtechweb.in


A
Alexei - check Codidact

在变量必须保持未初始化(并且在运行时处理)的情况下解决的另一种方法是将 undefined 添加到类型(这实际上是 VC Code 建议的)。例子:

@Input() availableData: HierarchyItem[] | undefined;
@Input() checkableSettings: CheckableSettings | undefined;

根据实际使用情况,这可能会导致其他问题,因此我认为最好的方法是尽可能初始化属性。


在初始化 @ViewChild 变量时为我工作。请参阅:stackblitz.com/edit/…
@Input() availableData: HierarchyItem[] | undefined; 与写 @Input() availableData?: HierarchyItem[] 相同;)
T
Takesha Kaluarachchi

新版本的 typescript 引入了严格的类初始化,这意味着您需要在构造函数主体中初始化类中的所有属性,或者通过属性初始化程序。 check it in typescript doccumntation 为避免这种情况,您可以使用属性添加(!或?)

make!: any[] or make? : any[] 

否则,如果您希望在项目中永久删除严格的类检查,您可以在 tsconfig.json 文件中设置 strictPropertyInitialization": false

"compilerOptions": { .... "noImplicitReturns": false, .... "strictPropertyInitialization": false },


'!' 和有什么区别?和 '?' ?
@pouyada ! 是自信的分配声明,表示无论分配什么,即使打字稿无法拾取它。 ? 是可选的,表示它可能存在也可能不存在,但您的组件应为任何一种情况做好准备。
A
Andy

你不能只使用一个明确的赋值断言吗? (见https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions

即,将属性声明为 makes!: any[]; !确保打字稿在运行时肯定会有一个值。

抱歉,我没有在 Angular 中尝试过这个,但是当我在 React 中遇到完全相同的问题时,它对我很有用。


使用所有版本,因为它在官方文档中并且我尝试过。谢谢!!
O
Onur Dikmen

1)您可以像下面的代码一样应用它。当你这样做时,系统不会给出错误。

“Definite Assignment Assertion”(!)告诉 TypeScript 我们知道这个值

Detail info

@Injectable()
export class Contact {
  public name !:string;
  public address!: Address;
  public digital!: Digital[];
  public phone!: Phone[];
}

2)第二种方法是在这里创建一个构造函数并定义值。

export class Contact {
  public name :string;
  public address: Address;
  public digital: Digital[];
  public phone: Phone[];

  constructor( _name :string,
     _address: Address,
     _digital: Digital[],
     _phone: Phone[])
  {
    this.name=_name;
    this.address=_address;
    this.digital=_digital;
    this.phone=_phone;
  }
}

3) 第三种选择是创建一个 get 属性并分配一个值,如下所示

  export class Contact {
      public name :string="";
      public address: Address=this._address;
    
      get _address(): Address {
        return new Address();
      }
     
    }

S
Sydney_dev

在 tsconfig.json 中添加这两行

"noImplicitReturns": true,
"strictPropertyInitialization": false,

并确保 strict 设置为 true


A
Abilogos

改变

fieldname?: any[]; 

对此:

fieldname?: any; 

k
keivan kashani

您可以像这样在构造函数中声明属性:

export class Test {
constructor(myText:string) {
this.myText= myText;
} 

myText: string ;
}

m
maxisam

这已在 Angular Github https://github.com/angular/angular/issues/24571 中讨论过

我想这就是每个人都会去的地方

引自https://github.com/angular/angular/issues/24571#issuecomment-404606595

For angular components, use the following rules in deciding between:
a) adding initializer
b) make the field optional
c) leave the '!'

If the field is annotated with @input - Make the field optional b) or add an initializer a).
If the input is required for the component user - add an assertion in ngOnInit and apply c.
If the field is annotated @ViewChild, @ContentChild - Make the field optional b).
If the field is annotated with @ViewChildren or @ContentChildren - Add back '!' - c).
Fields that have an initializer, but it lives in ngOnInit. - Move the initializer to the constructor.
Fields that have an initializer, but it lives in ngOnInit and cannot be moved because it depends on other @input fields - Add back '!' - c).

A
Alexey Ryzhkov

就我而言,它可以根据新的打字稿严格功能使用不同的声明:

@ViewChild(MatSort, {static: true}) sort!: MatSort;

如果在 tsonfig.json 中禁用打字稿新的严格功能

"compilerOptions": {
  ///
  ,
  "strictPropertyInitialization":false
}

旧 Angular 的指南代码运行良好

@ViewChild(MatSort) sort: MatSort;

感谢 Arunkumar Gudelli (2022),这里有 4 种解决问题的方法https://www.angularjswiki.com/angular/property-has-no-initializer-and-is-not-definitely-assigned-in-the-constructor/


T
Tabish Zaman

只需转到 tsconfig.ts 并将“strictPropertyInitialization”:false,添加到 compilerOptions 对象中。

如果还没有解决,请重新打开您的代码编辑器。

例子:

"compilerOptions" : {
  "strictPropertyInitialization": false,
}

S
Suresh

注释 tsconfig.json 文件中的 //"strict": true 行。


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅