Другое

Как исправить ошибку инициализации свойств TypeScript в Angular

Узнайте, как исправить ошибку TypeScript 'Свойство не имеет инициализатора' в компонентах Angular. Узнайте 4 эффективных решения с примерами и лучшими практиками для правильной инициализации свойств.

Как исправить ошибку TypeScript “Property has no initializer and is not definitely assigned in the constructor” в компоненте Angular?

Я получаю ошибку “Property ‘…’ has no initializer and is not definitely assigned in the constructor” в моем компоненте Angular. Вот мой код:

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

Что вызывает эту ошибку TypeScript и как правильно инициализировать свойство ‘makes’ в моем компоненте Angular?

Эта ошибка TypeScript возникает из-за строгих правил инициализации TypeScript, требующих, чтобы все свойства класса либо имели инициализаторы, либо были определенно присвоены в конструкторе. В вашем случае свойство makes объявлено без инициализатора и не присваивается в конструкторе, что вызывает ошибку компилятора.

Содержание


Понимание ошибки

Ошибка “Свойство ‘…’ не имеет инициализатора и не определенно присвоено в конструкторе” возникает из-за опции компилятора TypeScript strictPropertyInitialization, которая была введена в TypeScript 2.7. Эта функция требует, чтобы все свойства класса соответствовали одному из следующих условий:

  1. Иметь явный инициализатор (например, makes: any[] = [];)
  2. Быть присвоенным в конструкторе
  3. Использовать оператор не-null утверждения (!)

Это функция безопасности времени компиляции, которая помогает предотвратить ошибки времени выполнения, гарантируя, что свойства правильно инициализируются перед использованием. В вашем Angular-компоненте свойство makes нарушает это правило, так как оно объявлено без инициализатора и не присваивается в конструкторе.

typescript
// Это вызывает ошибку:
makes: any[]; // Нет инициализатора

Решение 1: Инициализировать свойство

Самый простой способ — предоставить значение по умолчанию для свойства во время объявления:

typescript
makes: any[] = []; // Инициализировать пустым массивом

Преимущества:

  • Чистый и читаемый код
  • Соответствует лучшим практикам TypeScript
  • Предотвращает ошибки времени выполнения с null-ссылками
  • Работает с обнаружением изменений Angular

Недостатки:

  • Всегда создает пустой массив, даже когда он не нужен сразу

Пример реализации:

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

Решение 2: Использовать оператор не-null утверждения

Вы можете использовать оператор не-null утверждения (!), чтобы сообщить TypeScript, что вы будете обрабатывать инициализацию:

typescript
makes: any[]!; // Оператор не-null утверждения

Преимущества:

  • Чистый синтаксис объявления
  • Явно указывает, что вы обрабатываете инициализацию
  • Хорошо работает, когда инициализация происходит в ngOnInit или хуках жизненного цикла

Недостатки:

  • Скрывает потенциальные ошибки времени выполнения, если инициализация не удалась
  • Менее безопасен с точки зрения типов, чем явная инициализация

Пример реализации:

typescript
export class VehicleFormComponent implements OnInit {
  makes: any[]!; // Оператор не-null утверждения
  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);
  }
}

Решение 3: Присвоить в конструкторе

Вы можете присвоить свойство в конструкторе, сделав его “определенно присвоенным”:

typescript
constructor(private makeService: MakeService) { 
  this.makes = []; // Инициализировать в конструкторе
}

Преимущества:

  • Явная инициализация
  • Гарантирует, что свойство доступно немедленно
  • Хорошие шаблоны внедрения зависимостей

Недостатки:

  • Конструктор может стать загроможденным из-за инициализации
  • Может быть неидеально для асинхронной инициализации (например, вызовы API)

Пример реализации:

typescript
export class VehicleFormComponent implements OnInit {
  makes: any[];
  vehicle = {};

  constructor(private makeService: MakeService) { 
    this.makes = []; // Инициализировать в конструкторе
  }

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

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

Решение 4: Отключить строгую инициализацию

Вы можете отключить проверку строгой инициализации свойств в вашем tsconfig.json:

json
{
  "compilerOptions": {
    "strictPropertyInitialization": false
  }
}

Преимущества:

  • Быстрое исправление для всего проекта
  • Позволяет более гибкие шаблоны инициализации

Недостатки:

  • Снижает безопасность типов
  • Может скрывать потенциальные ошибки времени выполнения
  • Не рекомендуется для новых проектов

Важное замечание: Как упоминается в Angular Wiki, этот подход “не будет хорошей идеей отключить строгую проверку для всего кодаbase, просто как исправление для одного файла.”


Лучшие практики и рекомендации

Какое решение выбрать?

Решение Когда использовать Рейтинг
Инициализировать значением по умолчанию В большинстве случаев, особенно для примитивных типов и массивов ⭐⭐⭐⭐⭐
Оператор не-null утверждения (!) Когда инициализация происходит в ngOnInit или хуках жизненного цикла ⭐⭐⭐⭐
Присвоение в конструкторе Для внедрения зависимостей и простой инициализации ⭐⭐⭐
Отключить strictPropertyInitialization Только в крайнем случае

Дополнительные рекомендации:

  1. Для @Input свойств: Используйте оператор не-null утверждения, так как вводы инициализируются фреймворком Angular:

    typescript
    @Input() public todo: Todo!; // Оператор не-null утверждения для @Input
    
  2. Для асинхронных свойств: Рассмотрите использование опциональной цепочки или проверок на null:

    typescript
    get firstMake(): string | undefined {
      return this.makes?.[0]?.name;
    }
    
  3. Для Observable свойств: Инициализируйте их в конструкторе или используйте оператор не-null утверждения:

    typescript
    private makes$ = new Subject<any[]>();
    
  4. Рассмотрите возможность сделать свойства опциональными: Если свойство может не всегда понадобиться, используйте опциональные свойства:

    typescript
    makes?: any[]; // Опциональное свойство
    

Полный исправленный пример

Вот ваш компонент с рекомендуемым решением (инициализация значением по умолчанию):

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

Это решение обеспечивает лучший баланс безопасности типов, читаемости и совместимости с Angular, следуя лучшим практикам TypeScript.

Источники

  1. Angular Wiki - Property has no initializer and is not definitely assigned in the constructor
  2. Stack Overflow - Property ‘…’ has no initializer and is not definitely assigned in the constructor
  3. Medium - How to fix error in angular has no initializer and is not definitely assigned in the constructor
  4. bobbyhadz - Property has no initializer and is not definitely assigned in the constructor
  5. Monster Lessons Academy - Angular Property has no initializer and is not definitely assigned in the constructor

Заключение

Ошибка TypeScript “Свойство не имеет инициализатора и не определенно присвоено в конструкторе” — это функция безопасности, обеспечивающая правильную инициализацию свойств класса. Для вашего Angular-компонента лучшим решением является инициализация свойства makes значением по умолчанию в виде пустого массива (makes: any[] = [];). Этот подход обеспечивает безопасность типов, предотвращает ошибки времени выполнения и соответствует лучшим практикам TypeScript. Хотя другие решения, такие как использование оператора не-null утверждения или отключение строгой инициализации, доступны, они имеют компромиссы в отношении безопасности типов и поддерживаемости кода. Выберите решение, которое лучше всего соответствует вашему конкретному случаю использования, сохраняя целостность конфигурации TypeScript.

Авторы
Проверено модерацией
Модерация