Właściwość „…” nie ma inicjatora i nie jest ostatecznie przypisana w konstruktorze

133

w mojej aplikacji Angular mam komponent:

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);
  }
}

ale we właściwości „czyni” mam błąd. Nie wiem, co z tym zrobić ...

błąd

Michael Kostiuchenko
źródło

Odpowiedzi:

79

Myślę, że używasz najnowszej wersji TypeScript. Zobacz sekcję „Ścisłe inicjowanie klas” w link.

Istnieją dwa sposoby, aby to naprawić:

O. Jeśli używasz VSCode, musisz zmienić wersję TS używaną przez edytor.

B. Po prostu zainicjalizuj tablicę, gdy zadeklarujesz ją wewnątrz konstruktora,

makes: any[] = [];

constructor(private makeService: MakeService) { 
   // Initialization inside the constructor
   this.makes = [];
}
Sajeetharan
źródło
przepraszam, jestem nowy w maszynopisie. Czy możesz powiedzieć, gdzie jest mój błąd?
Michael Kostiuchenko
2
ponieważ błąd mówi, że musisz zainicjować zmienną do jakiejś wartości sprawia: any [] = [];
Sajeetharan
1
Musisz użyć opcji „Definite Assignment Assertion”, aby poinformować maszynopis, że ta zmienna będzie miała wartość w czasie wykonywania w następujący sposób:makes!: any[];
Hussein Akar,
210

Po prostu przejdź do tsconfig.json i ustaw

"strictPropertyInitialization": false

aby pozbyć się błędu kompilacji.

W przeciwnym razie musisz zainicjalizować wszystkie swoje zmienne, co jest trochę denerwujące

Martin Čuka
źródło
3
Tylko upewnij się, że dodałeś, że po wyrażeniu „ścisłe”: prawda, w przeciwnym razie transpiler wydaje się ponownie go włączać (chociaż VS wydaje się wiedzieć, że jest wyłączony).
poniedziałek
52
W ten sposób wyłączysz inicjalizację ścisłego sprawdzania właściwości dla całego projektu. Lepiej jest dodać !operator przyrostka do nazwy zmiennej, aby po prostu zignorować ten przypadek lub zainicjować zmienną wewnątrz konstruktora.
Matteo Guarnerio
10
Sugerujesz zignorowanie potencjalnych problemów, o których mówi kompilator, nie jest to naprawdę bezpieczne. Więc głosuj przeciw.
Olga,
6
StrictPropertyInitialzer to flaga wprowadzona w maszynie 2.7. Do każdego należy decyzja, czy chcesz włączyć wszystkie te flagi i ściśle przestrzegać wszystkich reguł, czy wyłączyć niektóre sprawdzanie poprawności. Nie ma sensu za każdym razem przestrzegać wszystkich zasad. Jeśli twój kod staje się zbyt rozwlekły i ma wady i jest większy niż zalety, zdecydowanie powinieneś go wyłączyć. Nie mówię, że jest to najlepsza praktyka we wszystkich przypadkach, ale w większości przypadków jest to zdecydowanie realna opcja ...
Martin Čuka
2
kiedy ustawię wartość na fałsz, nadal pojawia się ten sam błąd, jaki może być problem? Używam VS Code
sertsedat
67

Dzieje się tak, ponieważ TypeScript 2.7 zawiera ścisłe sprawdzanie klasy, gdzie wszystkie właściwości powinny zostać zainicjowane w konstruktorze. Obejściem problemu jest dodanie !jako przyrostka do nazwy zmiennej:

makes!: any[];
Harinath
źródło
12
Znak „!” składnia istnieje dla tych typowych przypadków, w których nie można zagwarantować, że wartość zostanie zdefiniowana natychmiast. Jest to luk ewakuacyjny i nie należy na nim polegać, ponieważ może uczynić twój kod mniej bezpiecznym. Zwykle preferowana jest wartość domyślna. Dobrze jednak wiedzieć, że istnieje
kingdaro
@kingdaro ma rację. Chociaż można tego ogólnie używać, może to również prowadzić do kodu, który nie działa. Na przykład w podstawowej aplikacji internetowej wygenerowanej przez VS2017 zmień przypisanie prognoz w pliku fetchdata.components.ts, dodając znak „!” (publiczne prognozy !: WeatherForecast [];) i spowoduje to całkowite wystąpienie błędu
IronRod
Jest to najlepsze rozwiązanie, ponieważ znajduje się bezpośrednio po dekoratorze @Input () (w nowym kącie), podczas odczytu dowolnego komponentu w sposób naturalny czyta i eliminuje wszelkie błędy deweloperów.
ELI7VH
23

Komunikat możemy otrzymać Property has no initializer and is not definitely assigned in the constructorpodczas dodawania konfiguracji w tsconfig.jsonpliku, aby projekt Angular był skompilowany w trybie ścisłym:

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

Rzeczywiście, kompilator następnie skarży się, że zmienna składowa nie jest zdefiniowana przed użyciem.

Na przykład zmiennej składowej, która nie jest zdefiniowana w czasie kompilacji, zmienna składowa mająca @Inputdyrektywę:

@Input() userId: string;

Moglibyśmy wyciszyć kompilator, stwierdzając, że zmienna może być opcjonalna:

@Input() userId?: string;

Ale wtedy musielibyśmy poradzić sobie z przypadkiem niezdefiniowanej zmiennej i zaśmiecać kod źródłowy takimi instrukcjami:

if (this.userId) {
} else {
}

Zamiast tego, wiedząc, że wartość tej zmiennej składowej zostanie zdefiniowana w czasie, to znaczy zostanie zdefiniowana przed użyciem, możemy powiedzieć kompilatorowi, aby nie martwił się, że nie zostanie zdefiniowana.

Sposobem na przekazanie tego kompilatorowi jest dodanie ! definite assignment assertionoperatora, na przykład:

@Input() userId!: string;

Teraz kompilator rozumie, że ta zmienna, chociaż nie została zdefiniowana w czasie kompilacji, powinna zostać zdefiniowana w czasie wykonywania i w czasie, zanim zostanie użyta.

Teraz aplikacja musi upewnić się, że ta zmienna została zdefiniowana przed użyciem.

Jako dodatkowe zabezpieczenie możemy zapewnić, że zmienna jest definiowana, zanim jej użyjemy.

Możemy stwierdzić, że zmienna jest zdefiniowana, to znaczy, że wymagane powiązanie wejściowe zostało faktycznie dostarczone przez kontekst wywołujący:

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();
}

Wiedząc, że zmienna została zdefiniowana, można jej teraz użyć:

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

Należy zauważyć, że ngOnInitmetoda jest wywoływana po próbie powiązań wejściowych, nawet jeśli żadne rzeczywiste dane wejściowe nie zostały dostarczone do powiązań.

Podczas gdy ngOnChangesmetoda jest wywoływana po próbie powiązań wejściowych i tylko wtedy, gdy rzeczywiste dane wejściowe zostały dostarczone do powiązań.

Stephane
źródło
to jest prawidłowa odpowiedź przy używaniu trybu „ścisłego”
RnDrx
Wiele z tego ma dla mnie sens oprócz jednej rzeczy ... Co dałoby ci to zapewnienie? Byłoby dobrze do debugowania, ale nie do korzystania z aplikacji. Zgłaszanie błędów, które nie zostaną wychwycone, nie jest dobrym pomysłem. Powinieneś zapewnić rezerwę, która sprawi, że wszystko będzie działać bez względu na wszystko.
Nux
8

Możesz również wykonać następujące czynności, jeśli naprawdę nie chcesz go inicjalizować.

makes?: any[];
SpeedOfSpin
źródło
8

Musisz albo wyłączyć --strictPropertyInitializationten Sajeetharan, o którym mowa, albo zrobić coś takiego, aby spełnić wymóg inicjalizacji:

makes: any[] = [];
kshetline
źródło
6

Począwszy od TypeScript 2.7.2, musisz zainicjować właściwość w konstruktorze, jeśli nie została ona przypisana w momencie deklaracji.

Jeśli pochodzisz z Vue, możesz spróbować następujących rzeczy:

  • Dodaj "strictPropertyInitialization": truedo swojego tsconfig.json

  • Jeśli nie jesteś zadowolony z wyłączenia go, możesz również spróbować tego makes: any[] | undefined. Aby to zrobić, musisz uzyskać dostęp do właściwości za pomocą ?.operatora null check ( ), tjthis.makes?.length

  • Możesz równie dobrze spróbować makes!: any[];, to mówi TS, że wartość zostanie przypisana w czasie wykonywania.
codejockie
źródło
4

Podczas uaktualniania przy użyciu [email protected] jego kompilator ściśle przestrzega zasad dla typu tablicowego zadeklarowanego wewnątrz konstruktora klasy komponentów.

Aby rozwiązać ten problem, zmień kod, w którym są zadeklarowane w kodzie, lub unikaj kompilatora, aby dodać właściwość „strictPropertyInitialization”: false w pliku „tsconfig.json” i uruchom ponownie npm start.

Angular Web and Mobile Application Development można przejść do www.jtechweb.in

Jai Govind Gupta
źródło
3

Czy nie możesz po prostu użyć określonego asercji przydziału? (Zobacz https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions )

tj. zadeklarowanie własności jako makes!: any[];The! zapewnia, że ​​maszynopis na pewno będzie miał wartość w czasie wykonywania.

Przepraszam, że nie próbowałem tego w trybie kątowym, ale działało świetnie, gdy miałem dokładnie ten sam problem w Reakcie.

Andy
źródło
Pracuję ze wszystkimi wersjami, ponieważ jest to w oficjalnej dokumentacji i wypróbowałem. DZIĘKI!!
Hussein Akar
3

Uzyskaj ten błąd podczas dodawania Node do mojego projektu Angular -

TSError:? Nie można skompilować TypeScript: (ścieżka) /base.api.ts:19:13 - błąd TS2564: Właściwość „apiRoot Path” nie ma inicjatora i nie jest ostatecznie przypisana w konstruktorze.

prywatny apiRootPath: string;

Rozwiązanie -

Dodano "strictPropertyInitialization": falsew „compilerOptions” pliku tsconfig.json .

my package.json -

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

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

Pinaki
źródło
2

Błąd jest uzasadniony i może uniemożliwić awarię aplikacji. Wpisałeś makesjako tablicę, ale może być również niezdefiniowany.

Masz 2 opcje (zamiast wyłączać przyczynę istnienia ...):

1. W twoim przypadku najlepiej jest wpisać makesmożliwie niezdefiniowane.

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

W takim przypadku kompilator poinformuje Cię za każdym razem, gdy spróbujesz uzyskać do makesniego dostęp , że może to być niezdefiniowane. Na przykład, jeśli napiszesz // <-- Not okponiższe wiersze przed getMakeszakończeniem lub jeśli się getMakesnie powiedzie, zostanie zgłoszony błąd kompilacji. W przeciwnym razie aplikacja ulegnie awarii.

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

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

2. Możesz założyć, że to się nigdy nie zawiedzie i nigdy nie będziesz próbował uzyskać do niego dostępu przed inicjalizacją, pisząc poniższy kod (ryzykowne!). Więc kompilator nie zajmie się tym.

makes!: any[]
Alexandre Annic
źródło
0

zmienić

fieldname?: any[]; 

do tego:

fieldname?: any; 
MAM.AASIM
źródło