คุณสมบัติ '…' ไม่มีตัวเริ่มต้นและไม่ได้กำหนดไว้อย่างแน่นอนในตัวสร้าง


134

ในแอป 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);
  }
}

แต่ในคุณสมบัติ "ทำให้" ฉันมีข้อผิดพลาด ฉันไม่รู้จะทำอย่างไรกับมัน ...

ความผิดพลาด

คำตอบ:


81

ฉันคิดว่าคุณกำลังใช้ TypeScript เวอร์ชันล่าสุด โปรดดูส่วน "การเริ่มต้นคลาสที่เข้มงวด" ในไฟล์link.

มีสองวิธีในการแก้ไขปัญหานี้:

ตอบหากคุณใช้ VSCode คุณต้องเปลี่ยนเวอร์ชัน TS ที่ตัวแก้ไขใช้

B. เพียงเริ่มต้นอาร์เรย์เมื่อคุณประกาศภายในตัวสร้าง

makes: any[] = [];

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

ขออภัยฉันใหม่ใน typescript บอกได้ไหมว่าฉันผิดพลาดตรงไหน?
Michael Kostiuchenko

2
เนื่องจากข้อผิดพลาดระบุว่าคุณต้องเริ่มต้นตัวแปรให้เป็นค่าที่ทำให้: any [] = [];
Sajeetharan

1
คุณต้องใช้ 'Definite Assignment Assertion' เพื่อบอก typescript ว่าตัวแปรนี้จะมีค่าที่รันไทม์ดังนี้:makes!: any[];
Hussein Akar

211

เพียงไปที่tsconfig.jsonและตั้งค่า

"strictPropertyInitialization": false

เพื่อกำจัดข้อผิดพลาดในการคอมไพล์

มิฉะนั้นคุณจะต้องเริ่มต้นตัวแปรทั้งหมดของคุณซึ่งน่ารำคาญเล็กน้อย


4
ตรวจสอบให้แน่ใจว่าคุณเพิ่มหลังจาก "เข้มงวด": จริงมิฉะนั้นทรานสไพเลอร์ดูเหมือนจะเปิดอีกครั้ง (แม้ว่า VS ดูเหมือนจะรู้ว่าปิดอยู่ก็ตาม)
monty

52
ด้วยวิธีนี้คุณจะปิดใช้งานการเริ่มต้นคุณสมบัติการตรวจสอบอย่างเข้มงวดสำหรับโครงการทั้งหมด จะเป็นการดีกว่าที่จะเพิ่มตัว!ดำเนินการ postfix ลงในชื่อตัวแปรเพื่อละเว้นกรณีนี้หรือเริ่มต้นตัวแปรภายในตัวสร้าง
Matteo Guarnerio

10
คุณกำลังแนะนำให้เพิกเฉยต่อปัญหาที่อาจเกิดขึ้นซึ่งคอมไพเลอร์กำลังบอกอยู่นี้ไม่ปลอดภัยจริงๆ โหวตลงเลย
Olga

7
StrictPropertyInitialzer เป็นแฟล็กที่นำมาใช้ใน typescript 2.7 ขึ้นอยู่กับทุกคนที่จะเลือกว่าคุณต้องการเปิดใช้แฟล็กเหล่านี้ทั้งหมดหรือไม่และปฏิบัติตามกฎทั้งหมดอย่างเคร่งครัดหรือปิดการตรวจสอบความถูกต้องบางอย่าง มันไม่สมเหตุสมผลเลยที่จะปฏิบัติตามกฎทั้งหมดทุกครั้ง หากโค้ดของคุณมีรายละเอียดและข้อเสียมากเกินไปและใหญ่กว่าข้อดีคุณควรปิดใช้งานอย่างแน่นอน ฉันไม่ได้บอกว่านี่เป็นแนวทางปฏิบัติที่ดีที่สุดในทุกกรณี แต่เป็นทางเลือกที่ใช้ได้จริงในกรณีส่วนใหญ่ ...
Martin Čuka

2
ดังที่ฉันได้กล่าวไว้ในคำตอบของฉันคุณมีสองตัวเลือกไม่ว่าคุณจะปิดใช้งานการตรวจสอบความถูกต้องหรือเริ่มต้นตัวแปรทั้งหมด ขึ้นอยู่กับทุกคนที่จะเลือกสิ่งที่คุณจะได้รับประโยชน์มากขึ้นจากโครงการต่อโครงการ
Martin Čuka

68

เป็นเพราะ TypeScript 2.7 มีการตรวจสอบคลาสที่เข้มงวดซึ่งคุณสมบัติทั้งหมดควรถูกเตรียมใช้งานในตัวสร้าง วิธีแก้ปัญหาคือการเพิ่ม!เป็น postfix ให้กับชื่อตัวแปร:

makes!: any[];

12
การ "!" ไวยากรณ์มีอยู่สำหรับกรณีทั่วไปที่คุณไม่สามารถรับประกันได้ว่าค่าจะถูกกำหนดทันที มันเป็นช่องทางหลบหนีและไม่ควรพึ่งพาเนื่องจากจะทำให้รหัสของคุณปลอดภัยน้อยลง โดยปกติค่าเริ่มต้นจะเป็นที่ต้องการ น่ารู้ว่ามีอยู่จริง
kingdaro

@kingdaro พูดถูก แม้ว่าโดยทั่วไปจะสามารถใช้งานได้ แต่ก็สามารถนำไปสู่รหัสที่แบนไม่ทำงาน ตัวอย่างเช่นใน webapp พื้นฐานที่สร้างโดย VS2017 ให้เปลี่ยนการกำหนดสำหรับการคาดการณ์ใน fetchdata.components.ts โดยการเพิ่ม "!" (การคาดการณ์สาธารณะ!: WeatherForecast [];) และจะทำให้เกิดข้อผิดพลาดอย่างสมบูรณ์
IronRod

นี่เป็นทางออกที่ดีที่สุดเนื่องจากอยู่หลังมัณฑนากร @Input () โดยตรง (ในเชิงมุมใหม่) เมื่ออ่านส่วนประกอบใด ๆ ก็ตามมันจะอ่านอย่างเป็นธรรมชาติและกำจัดข้อผิดพลาดของการพัฒนาใด ๆ
ELI7VH

23

เราอาจได้รับข้อความProperty has no initializer and is not definitely assigned in the constructorเมื่อเพิ่มการกำหนดค่าบางอย่างในtsconfig.jsonไฟล์เพื่อให้มีการคอมไพล์โครงการ 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เมธอดถูกเรียกหลังจากความพยายามในการเชื่อมโยงอินพุตและเฉพาะในกรณีที่มีอินพุตจริงที่จัดเตรียมไว้ให้กับการโยง


นี่คือคำตอบที่ถูกต้องเมื่อใช้โหมด 'เข้มงวด'
RnDrx

เรื่องนี้เข้าท่าสำหรับฉันนอกเหนือจากสิ่งหนึ่ง ... คำยืนยันจะให้อะไรคุณ? จะดีสำหรับการดีบัก แต่ไม่ใช่สำหรับการใช้แอป การโยนข้อผิดพลาดที่จะไม่ถูกจับไม่ใช่ความคิดที่ดี คุณควรระบุทางเลือกอื่นที่จะทำให้สิ่งต่างๆทำงานได้ไม่ว่าจะเกิดอะไรขึ้นก็ตาม
Nux

8

คุณยังสามารถทำสิ่งต่อไปนี้ได้หากคุณไม่ต้องการเริ่มต้นจริงๆ

makes?: any[];

8

คุณจำเป็นต้องปิดการใช้งาน--strictPropertyInitializationที่ Sajeetharan อ้างถึงหรือทำสิ่งนี้เพื่อตอบสนองความต้องการในการเริ่มต้น:

makes: any[] = [];

6

ใน TypeScript 2.7.2 คุณจะต้องเริ่มต้นคุณสมบัติในตัวสร้างหากไม่ได้กำหนดให้ ณ จุดที่ประกาศ

หากคุณมาจาก Vue คุณสามารถลองทำสิ่งต่อไปนี้:

  • เพิ่ม"strictPropertyInitialization": trueใน tsconfig.json ของคุณ

  • หากคุณไม่พอใจกับการปิดการใช้งานคุณสามารถลองสิ่งนี้makes: any[] | undefinedได้ การทำเช่นนี้ต้องการให้คุณเข้าถึงคุณสมบัติด้วย?.ตัวดำเนินการcheck ( ) null เช่นthis.makes?.length

  • คุณสามารถลองเช่นกันmakes!: any[];สิ่งนี้บอก TS ว่าค่าจะถูกกำหนดในรันไทม์

4

เมื่อคุณอัปเกรดโดยใช้ typescript@2.9.2 คอมไพเลอร์จะเข้มงวดกฎต่อไปนี้สำหรับประเภทอาร์เรย์ที่ประกาศภายในตัวสร้างคลาสคอมโพเนนต์

สำหรับการแก้ไขปัญหานี้ให้เปลี่ยนรหัสที่ประกาศในโค้ดหรือหลีกเลี่ยงการคอมไพเลอร์เพื่อเพิ่มคุณสมบัติ"tightPropertyInitialization": falseในไฟล์"tsconfig.json"และรัน npm start

การพัฒนาแอปพลิเคชันเว็บและมือถือเชิงมุมคุณสามารถไปที่ www.jtechweb.in


3

คุณไม่สามารถใช้การยืนยันการมอบหมายงานที่ชัดเจนได้หรือไม่? (ดูhttps://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions )

กล่าวคือประกาศทรัพย์สินเป็นmakes!: any[];The! รับรองว่า typescript จะมีค่าที่รันไทม์อย่างแน่นอน

ขออภัยฉันไม่ได้ลองสิ่งนี้ในเชิงมุม แต่มันใช้งานได้ดีสำหรับฉันเมื่อฉันมีปัญหาเดียวกันใน React


ใช้งานได้กับทุกเวอร์ชันเนื่องจากอยู่ในเอกสารอย่างเป็นทางการและฉันได้ลองใช้แล้ว ขอบคุณ !!
Hussein Akar

3

รับข้อผิดพลาดนี้ในขณะที่เพิ่ม Node ในโครงการ Angular ของฉัน -

TSError:? ไม่สามารถคอมไพล์ TypeScript: (path) /base.api.ts:19:13 - ข้อผิดพลาด TS2564: คุณสมบัติ 'apiRoot Path' ไม่มีตัวเริ่มต้นและไม่ได้กำหนดไว้อย่างแน่นอนในตัวสร้าง

apiRootPath ส่วนตัว: string;

วิธีการแก้ -

เพิ่ม"strictPropertyInitialization": falseใน 'compilerOptions' ของ tsconfig.json

package.jsonของฉัน-

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

อ้างอิง URL - https://www.ryadel.com/en/ts2564-ts-property-has-no-initializer-typescript-error-fix-visual-studio-2017-vs2017/


2

ข้อผิดพลาดนี้ถูกต้องและอาจป้องกันไม่ให้แอปของคุณขัดข้อง คุณพิมพ์makesเป็นอาร์เรย์ แต่ยังไม่สามารถกำหนดได้

คุณมี 2 ตัวเลือก (แทนที่จะปิดใช้งานเหตุผลของ typescript ที่มีอยู่ ... ):

1.ในกรณีของคุณวิธีที่ดีที่สุดคือการพิมพ์makesโดยไม่ได้กำหนดไว้

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

ในกรณีนี้คอมไพลเลอร์จะแจ้งให้คุณทราบเมื่อใดก็ตามที่คุณพยายามเข้าถึงmakesซึ่งอาจไม่ได้กำหนดไว้ ตัวอย่างเช่นหากคุณเขียน// <-- Not okบรรทัดด้านล่างก่อนที่จะgetMakesเสร็จสิ้นหรือหากgetMakesล้มเหลวข้อผิดพลาดในการคอมไพล์จะถูกโยนทิ้ง มิฉะนั้นแอปพลิเคชันของคุณจะขัดข้อง

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

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

2.คุณสามารถสันนิษฐานได้ว่าจะไม่มีวันล้มเหลวและคุณจะไม่พยายามเข้าถึงก่อนเริ่มต้นด้วยการเขียนโค้ดด้านล่าง (เสี่ยง!) ดังนั้นคอมไพเลอร์จะไม่สนใจมัน

makes!: any[]

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.