属性 '...' 没有初始化器,也没有在构造函数中明确分配
Posted
技术标签:
【中文标题】属性 \'...\' 没有初始化器,也没有在构造函数中明确分配【英文标题】:Property '...' has no initializer and is not definitely assigned in the constructor属性 '...' 没有初始化器,也没有在构造函数中明确分配 【发布时间】:2021-08-14 01:39:04 【问题描述】:在我的 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);
但是在“makes”属性中我有一个错误。 我不知道该怎么处理它......
【问题讨论】:
【参考方案1】:只需转到 tsconfig.json 并设置
"strictPropertyInitialization": false
摆脱编译错误。
否则你需要初始化所有变量,这有点烦人
【讨论】:
只要确保在 "strict": true 之后添加它,否则转译器似乎会再次打开它(尽管 VS 似乎知道它已关闭)。 这样你将禁用所有项目的严格检查属性初始化。最好将!
后缀运算符添加到变量名中,忽略这种情况,或者在构造函数中初始化变量。
您建议忽略编译器所说的潜在问题,这并不安全。所以投反对票。
StrictPropertyInitialzer 是 typescript 2.7 中引入的标志。每个人都可以选择是否要启用所有这些标志并严格遵守所有规则或关闭某些验证。每次都遵守所有规则是完全没有意义的。如果您的代码变得过于冗长和缺点并且大于优点,您应该明确地将其关闭。我并不是说这在所有情况下都是最佳做法,但在大多数情况下它绝对是可行的选择......
正如我在回答中提到的,您有两个选择,要么禁用验证,要么初始化所有变量。由每个人来选择您将从项目中受益更多的项目。【参考方案2】:
我认为您使用的是最新版本的 TypeScript。请参阅 link
中的“严格类初始化”部分。
有两种方法可以解决这个问题:
A.如果您使用的是 VSCode,则需要更改编辑器使用的 TS 版本。
B.声明时初始化数组即可
makes: any[] = [];
或在构造函数内部:
constructor(private makeService: MakeService)
// Initialization inside the constructor
this.makes = [];
【讨论】:
对不起,我是打字稿的新手。你能说我的错误在哪里吗? 因为错误表明您需要将变量初始化为某个值使得:any[] = []; 你必须使用'Definite Assignment Assertion'来告诉typescript这个变量在运行时会有一个值,如下:makes!: any[];
嗨@Sajeetharan,如何更改 vscode ts 版本
在构造函数中初始化变量是 Angular 的反模式。不要这样做。【参考方案3】:
这是因为 TypeScript 2.7 包含一个严格的类检查,所有属性都应该在构造函数中初始化。一种解决方法是添加
!
作为变量名的后缀:
makes!: any[];
【讨论】:
“!”对于那些无法保证立即定义值的常见情况,存在语法。这是一个逃生舱,不应该依赖它,因为它会使您的代码不那么安全。默认值通常是首选。不过很高兴知道它的存在 @kingdaro 是对的。虽然这通常可以使用,但它也可能导致 flat 不起作用的代码。例如,在 VS2017 生成的基本 webapp 中,通过添加“!”更改 fetchdata.components.ts 中预测的分配(公共预测!:WeatherForecast[];)这将导致它完全出错 这是最好的解决方案,因为它直接在 @Input() 装饰器之后(在新的角度),当读取任何给定的组件时,它会自然地读取并剔除任何开发错误。跨度> 顺便说一下和使用有什么区别!要么 ?处理空值或未定义? 我需要在应用程序的服务中定义一个使用枚举的属性,我不能假设用户的选择,甚至不是默认值。它是在第一步中定义的,直到第二步才被读取,所以这是非常有用的转义,很高兴知道它的存在,非常感谢。【参考方案4】:我们在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
方法是在输入绑定尝试之后调用的,并且只有在绑定提供了实际输入时才调用。
【讨论】:
这是使用“严格”模式时的正确答案 这对我来说很有意义,除了一件事......断言会给你什么?可以进行调试,但不适用于使用该应用程序。抛出不会被捕获的错误不是一个好主意。您应该提供一些后备措施,无论如何都会使事情正常进行。 这应该被认为是正确的答案,因为它解决了问题,而不仅仅是转动部分棉绒。 在严格模式下使用“确定赋值断言”只是为了避免条件检查没有多大意义。您正在破坏拥有严格类型系统的整个逻辑。如果避免这些检查对您来说更重要,那么您可能不应该使用严格模式。【参考方案5】:转到您的 tsconfig.json
文件并更改属性:
"noImplicitReturns": false
然后添加
"strictPropertyInitialization": false
在"compilerOptions"
属性下。
您的tsconfig.json
文件应如下所示:
...
"compilerOptions":
....
"noImplicitReturns": false,
....
"strictPropertyInitialization": false
,
"angularCompilerOptions":
......
希望这会有所帮助!
祝你好运
【讨论】:
IMO,有点倒退了。 首先,感谢您的解决方案,它奏效了。其次,谁能解释一下刚刚发生的事情? 成功了,在Angular中创建指令时很有必要。【参考方案6】:错误是合法,可能会阻止您的应用崩溃。您将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[]
更具体地说,
您的设计可能会更好。定义局部和可变变量不是一个好习惯。您应该管理服务内的数据存储:
首先是 nullsafe, 其次是能够分解大量代码(包括输入、加载状态和错误) 最后要避免重复和无用的 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 有点可怕。而且项目越大,你就越需要严格遵守类型规则。【参考方案7】:2021 年更新:
有类似“strictPropertyInitialization”的属性
只要去 tsconfig.json 设置
“严格”:假
摆脱编译错误。
否则你需要初始化所有变量,这有点烦人。
出现此错误的原因是:
typescript 是一种比 javascript 更安全的语言。 虽然通过启用 严格功能 可以增强这种安全性。所以每次初始化变量时,打字稿都希望它们分配一个值。【讨论】:
简单快捷的修复方法 完美运行。【参考方案8】:您要么需要禁用--strictPropertyInitialization
Sajeetharan 提到,或者做这样的事情来满足初始化要求:
makes: any[] = [];
【讨论】:
【参考方案9】:如果你真的不想初始化它,你也可以这样做。
makes?: any[];
【讨论】:
【参考方案10】:如果你想初始化一个基于接口的对象,你可以用下面的语句将其初始化为空。
myObj: IMyObject = as IMyObject;
【讨论】:
【参考方案11】:在使变量之后放一个问号(?)。
makes?: any[];
vehicle = ;
constructor(private makeService: MakeService)
它现在应该可以工作了。 我正在使用 angular 12,它适用于我的代码。
【讨论】:
【参考方案12】:从 TypeScript 2.7.2 开始,如果在声明时未分配属性,则需要在构造函数中初始化该属性。
如果你来自 Vue,你可以尝试以下方法:
将"strictPropertyInitialization": true
添加到您的 tsconfig.json
如果您对禁用它不满意,也可以试试这个makes: any[] | undefined
。这样做需要您使用空检查 (?.
) 运算符访问属性,即 this.makes?.length
makes!: any[];
,它告诉 TS 该值将在运行时分配。
【讨论】:
【参考方案13】:在我的 Angular 项目中添加节点时出现此错误 -
TSError: ?无法编译 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/
【讨论】:
【参考方案14】:击球手方法是将感叹号添加到您确定它不应未定义或为空的变量的末尾,例如您正在使用需要从模板加载的 ElementRef 并且可以'不在构造函数中定义,请执行以下操作
class Component
ViewChild('idname') variable! : ElementRef;
【讨论】:
【参考方案15】:当你使用 typescript@2.9.2 升级时,它的编译器严格遵循组件类构造函数中数组类型声明的规则。
要解决此问题,请更改代码中声明的代码或避免编译器在 "tsconfig.json" 中添加属性 "strictPropertyInitialization": false文件并再次运行 npm start 。
Angular web 和移动应用开发你可以去 www.jtechweb.in
【讨论】:
【参考方案16】:在变量必须保持未初始化(并在运行时处理)的情况下解决的另一种方法是将undefined
添加到类型(这实际上是 VC 代码建议的)。示例:
@Input() availableData: HierarchyItem[] | undefined;
@Input() checkableSettings: CheckableSettings | undefined;
根据实际使用情况,这可能会导致其他问题,所以我认为最好的方法是尽可能初始化属性。
【讨论】:
在初始化 @ViewChild 变量时为我工作。请参阅:stackblitz.com/edit/… 写@Input() availableData: HierarchyItem[] | undefined;
和写@Input() availableData?: HierarchyItem[]
是一样的;)【参考方案17】:
你不能只使用一个明确的赋值断言吗? (见https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions)
即将属性声明为 makes!: any[];
!确保 typescript 在运行时肯定会有一个值。
抱歉,我没有在 Angular 中尝试过这个,但是当我在 React 中遇到完全相同的问题时,它对我很有用。
【讨论】:
使用所有版本,因为它在官方文档中并且我尝试过。谢谢!!【参考方案18】:新版本的 typescript 引入了类初始化,这意味着您需要在构造函数主体中初始化类中的所有属性,或者通过属性初始化程序。 check it in typescript doccumntation 为避免这种情况,您可以使用属性添加(!或?)
make!: any[] or make? : any[]
否则,如果您希望在您的项目中永久删除严格的类检查 您可以在 tsconfig.json 文件中设置 strictPropertyInitialization": false
“编译器选项”: …… “noImplicitReturns”:假, …… “严格属性初始化”:假 ,
【讨论】:
'!' 和有什么区别和 '?' ? @pouyada!
是自信的赋值声明,表示无论分配什么,即使打字稿无法提取它。 ?
是可选的,意味着它可能存在也可能不存在,但您的组件应该为任何一种情况做好准备。【参考方案19】:
在 tsconfig.json 中添加这两行
"noImplicitReturns": true,
"strictPropertyInitialization": false,
并确保 strict 设置为 true
【讨论】:
【参考方案20】:改变
fieldname?: any[];
到这里:
fieldname?: any;
【讨论】:
【参考方案21】:注释 tsconfig.json 文件中的//"strict": true
行。
【讨论】:
【参考方案22】:这已在 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).
【讨论】:
【参考方案23】:在 tsconfig.json 文件中,在“compilerOptions”里面添加:
"strictPropertyInitialization": false,
【讨论】:
【参考方案24】:如果你不想改变你的“tsconfig.json”,你可以这样定义你的类:
class Adress
street: string = ''
或者,您也可以这样进行:
class Address
street!: string
通过添加感叹号“!”在你的变量名之前,Typescript 会确保这个变量不为空或未定义
【讨论】:
【参考方案25】:你可以像这样在构造函数中声明属性:
export class Test
constructor(myText:string)
this.myText= myText;
myText: string ;
【讨论】:
【参考方案26】:只需转到 tsconfig.ts 并添加 "strictPropertyInitialization": false, 放入 compilerOptions 对象中。
如果还没有解决,请重新打开您的代码编辑器。示例:
"compilerOptions" :
"strictPropertyInitialization": false,
【讨论】:
【参考方案27】:在变量“?”旁边您可以通过放置它来修复它。
例子:
--------->id?:number --------->姓名?:字符串
【讨论】:
以上是关于属性 '...' 没有初始化器,也没有在构造函数中明确分配的主要内容,如果未能解决你的问题,请参考以下文章
java中静态方法,静态变量,静态初始化器,构造函数,属性初始化都是啥时候调用的? 它们的先后顺序。
错误 C2512:没有适当的默认构造函数可用 - 为啥在构造函数中初始化属性?
角度错误 TS2564:属性“用户”没有初始化程序,并且未在构造函数中明确分配
Spring HATEOAS RepresentationModel,无法设置属性链接,因为没有设置器,没有枯萎,它不是持久性构造函数的一部分