Перейти к содержанию

Ошибки метаданных AOT

📅 28.02.2022

Ниже перечислены ошибки метаданных, с которыми вы можете столкнуться, с пояснениями и предлагаемыми исправлениями.

Форма выражения не поддерживается

Компилятор столкнулся с непонятным выражением при оценке метаданных Angular.

Особенности языка, выходящие за рамки ограниченного синтаксиса выражения компилятора, могут вызвать эту ошибку, как показано в следующем примере:

1
2
3
4
5
6
7
8
// ERROR
export class Fooish {  }

const prop = typeof Fooish; // typeof is not valid in metadata
  
  // bracket notation is not valid in metadata
  { provide: 'token', useValue: { [prop]: 'value' } };
  

Вы можете использовать typeof и скобочную нотацию в обычном коде приложения. Вы просто не можете использовать эти функции в выражениях, определяющих метаданные Angular.

Избежать этой ошибки можно, придерживаясь ограниченного синтаксиса выражений компилятора при написании метаданных Angular

и остерегайтесь новых или необычных функций TypeScript.

Ссылка на локальный (неэкспортированный) символ

Ссылка на локальный (неэкспортируемый) символ 'имя символа'. Рассмотрите возможность экспорта символа.

Компилятор столкнулся со ссылкой на локально определенный символ, который либо не экспортирован, либо не инициализирован.

Вот пример проблемы в provider.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// ERROR
let foo: number; // neither exported nor initialized

@Component({
  selector: 'my-component',
  template:  ,
  providers: [
    { provide: Foo, useValue: foo }
  ]
})
export class MyComponent {}

Компилятор генерирует фабрику компонентов, которая включает код поставщика useValue, в отдельном модуле. Этот модуль фабрики не может вернуться в исходный модуль, чтобы получить доступ к локальной (неэкспортируемой) переменной foo.

Вы можете решить проблему, инициализировав foo.

1
let foo = 42; // initialized

Компилятор свернет выражение в провайдер, как если бы вы написали это.

1
providers: [{ provide: Foo, useValue: 42 }];

Альтернативно, вы можете исправить это, экспортируя foo с расчетом на то, что foo будет присвоено во время выполнения, когда вы действительно будете знать его значение.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// CORRECTED
export let foo: number; // exported

@Component({
  selector: 'my-component',
  template:  ,
  providers: [
    { provide: Foo, useValue: foo }
  ]
})
export class MyComponent {}

Добавление export часто работает для переменных, на которые ссылаются в метаданных, таких как providers и animations, потому что компилятор может генерировать ссылки на экспортируемые переменные в этих выражениях. Ему не нужны значения этих переменных.

Добавление export не работает, когда компилятору нужно фактическое значение для генерации кода.

Например, это не работает для свойства template.

1
2
3
4
5
6
7
8
// ERROR
export let someTemplate: string; // exported but not initialized

@Component({
    selector: 'my-component',
    template: someTemplate,
})
export class MyComponent {}

Компилятору необходимо значение свойства template прямо сейчас для создания фабрики компонентов. Одной ссылки на переменную недостаточно. Префикс объявления с export просто выдает новую ошибку, "Only initialized variables and constants can be referenced".

Только инициализированные переменные и константы

На только инициализированные переменные и константы можно ссылаться, потому что значение этой переменной необходимо компилятору шаблона.

Компилятор обнаружил ссылку на экспортируемую переменную или статическое поле, которое не было инициализировано. Для генерации кода ему необходимо значение этой переменной.

Следующий пример пытается установить свойство компонента template в значение экспортированной переменной someTemplate, которая объявлена, но не назначена.

1
2
3
4
5
6
7
8
// ERROR
export let someTemplate: string;

@Component({
    selector: 'my-component',
    template: someTemplate,
})
export class MyComponent {}

Вы также получите эту ошибку, если импортируете someTemplate из другого модуля и не инициализируете его там.

1
2
3
4
5
6
7
8
// ERROR - not initialized there either
import { someTemplate } from './config';

@Component({
    selector: 'my-component',
    template: someTemplate,
})
export class MyComponent {}

Компилятор не может дожидаться времени выполнения, чтобы получить информацию о шаблоне. Он должен статически получить значение переменной someTemplate из исходного кода, чтобы сгенерировать фабрику компонентов, которая включает инструкции по созданию элемента на основе шаблона.

Чтобы исправить эту ошибку, укажите начальное значение переменной в предложении инициализатора в той же строке.

1
2
3
4
5
6
7
8
// CORRECTED
export let someTemplate = '<h1>Greetings from Angular</h1>';

@Component({
    selector: 'my-component',
    template: someTemplate,
})
export class MyComponent {}

Ссылка на неэкспортированный класс

Ссылка на неэкспортированный класс <имя класса>. Рассмотрите возможность экспорта класса.

Метаданные ссылаются на класс, который не был экспортирован.

Например, вы могли определить класс и использовать его в качестве маркера инъекции в массиве провайдеров, но не экспортировать этот класс.

1
2
3
4
5
6
7
8
// ERROR
abstract class MyStrategy { }

  // …
  providers: [
    { provide: MyStrategy, useValue:  }
  ]
  // …

Angular создает фабрику классов в отдельном модуле, и эта фабрика может получить доступ только к экспортированным классам. Чтобы исправить эту ошибку, экспортируйте ссылающийся класс.

1
2
3
4
5
6
7
8
// CORRECTED
export abstract class MyStrategy { }

  
  providers: [
    { provide: MyStrategy, useValue:  }
  ]
  

Ссылка на неэкспортированную функцию

Метаданные ссылались на функцию, которая не была экспортирована.

Например, вы могли установить свойство провайдера useFactory на локально определенную функцию, которую вы забыли экспортировать.

1
2
3
4
5
6
7
8
// ERROR
function myStrategy() {  }

  
  providers: [
    { provide: MyStrategy, useFactory: myStrategy }
  ]
  

Angular создает фабрику классов в отдельном модуле, и эта фабрика может получить доступ только к экспортированным функциям. Чтобы исправить эту ошибку, экспортируйте функцию.

1
2
3
4
5
6
7
8
// CORRECTED
export function myStrategy() {  }

  
  providers: [
    { provide: MyStrategy, useFactory: myStrategy }
  ]
  

Вызовы функций не поддерживаются

Вызовы функций не поддерживаются. Подумайте о замене функции или лямбды ссылкой на экспортируемую функцию.

В настоящее время компилятор не поддерживает функциональные выражения или лямбда-функции. Например, вы не можете установить useFactory провайдера на анонимную функцию или стрелочную функцию, как это сделано здесь.

1
2
3
4
5
6
7
// ERROR
  
  providers: [
    { provide: MyStrategy, useFactory: function() {  } },
    { provide: OtherStrategy, useFactory: () => {  } }
  ]
  

Вы также получите эту ошибку, если вызовете функцию или метод в useValue провайдера.

1
2
3
4
5
6
7
8
// ERROR
import { calculateValue } from './utilities';

  
  providers: [
    { provide: SomeValue, useValue: calculateValue() }
  ]
  

Чтобы исправить эту ошибку, экспортируйте функцию из модуля и обратитесь к ней в провайдере useFactory.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// CORRECTED
import { calculateValue } from './utilities';

export function myStrategy() {  }
export function otherStrategy() {  }
export function someValueFactory() {
  return calculateValue();
}
  
  providers: [
    { provide: MyStrategy, useFactory: myStrategy },
    { provide: OtherStrategy, useFactory: otherStrategy },
    { provide: SomeValue, useFactory: someValueFactory }
  ]
  

Деструктурированная переменная или константа не поддерживается

Ссылка на экспортируемую деструктурированную переменную или константу не поддерживается компилятором шаблонов. Рассмотрите возможность упрощения, чтобы избежать деструктуризации.

Компилятор не поддерживает ссылки на переменные, назначенные деструктуризацией.

Например, вы не можете написать что-то вроде этого:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// ERROR
import { configuration } from './configuration';

// destructured assignment to foo and bar
const {foo, bar} = configuration;
  
  providers: [
    {provide: Foo, useValue: foo},
    {provide: Bar, useValue: bar},
  ]
  

Чтобы исправить эту ошибку, обратитесь к неструктурированным значениям.

1
2
3
4
5
6
7
8
// CORRECTED
import { configuration } from './configuration';
  
  providers: [
    {provide: Foo, useValue: configuration.foo},
    {provide: Bar, useValue: configuration.bar},
  ]
  

Не удалось определить тип

Компилятор встретил тип и не может определить, какой модуль экспортирует этот тип.

Это может произойти, если вы ссылаетесь на окружающий тип. Например, тип Window является окружающим типом, объявленным в глобальном файле .d.ts.

Вы получите ошибку, если обратитесь к нему в конструкторе компонента, который компилятор должен статически анализировать.

1
2
3
4
5
// ERROR
@Component({ })
export class MyComponent {
  constructor (private win: Window) {  }
}

TypeScript понимает окружающие типы, поэтому вы не импортируете их. Компилятор Angular не понимает тип, который вы пренебрегаете экспортом или импортом.

В данном случае компилятор не понимает, как внедрить что-то с помощью маркера Window.

Не ссылайтесь на окружающие типы в выражениях метаданных.

Если вам необходимо ввести экземпляр окружающего типа, вы можете решить эту проблему в четыре шага:

  1. Создайте инъекционный маркер для экземпляра окружающего типа.

  2. Создайте фабричную функцию, которая возвращает этот экземпляр.

  3. Добавьте провайдер useFactory к этой заводской функции.

  4. Используйте @Inject для инъекции экземпляра.

Вот наглядный пример.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// CORRECTED
import { Inject } from '@angular/core';

export const WINDOW = new InjectionToken('Window');
export function _window() { return window; }

@Component({
  
  providers: [
    { provide: WINDOW, useFactory: _window }
  ]
})
export class MyComponent {
  constructor (@Inject(WINDOW) private win: Window) {  }
}

Тип Window в конструкторе больше не является проблемой для компилятора, потому что он использует @Inject(WINDOW) для генерации кода инъекции.

Angular делает нечто подобное с маркером DOCUMENT, чтобы вы могли внедрить объект document браузера (или его абстракцию, в зависимости от платформы, на которой работает приложение).

1
2
3
4
5
6
7
import { Inject }   from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Component({  })
export class MyComponent {
  constructor (@Inject(DOCUMENT) private doc: Document) {  }
}

Имя ожидается

Компилятор ожидал увидеть имя в выражении, которое он оценивал.

Это может произойти, если вы используете число в качестве имени свойства, как в следующем примере.

1
2
// ERROR
provider: [{ provide: Foo, useValue: { 0: 'test' } }];

Измените имя свойства на нечисловое.

1
2
// CORRECTED
provider: [{ provide: Foo, useValue: { '0': 'test' } }];

Неподдерживаемое имя члена перечисления

Angular не смог определить значение enum member, на которое вы ссылались в метаданных.

Компилятор может понимать простые значения перечислений, но не сложные значения, например, полученные из вычисляемых свойств.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// ERROR
enum Colors {
    Red = 1,
    White,
    Blue = 'Blue'.length, // computed
}

// …
providers: [
    { provide: BaseColor, useValue: Colors.White }, // ok
    { provide: DangerColor, useValue: Colors.Red }, // ok
    { provide: StrongColor, useValue: Colors.Blue }, // bad
];
// …

Избегайте ссылок на перечисления со сложными инициализаторами или вычисляемыми свойствами.

Выражения шаблонов с метками не поддерживаются

Тегированные шаблонные выражения не поддерживаются в метаданных.

Компилятор встретил JavaScript ES2015 tagged template expression, такой как следующий.

1
2
3
4
5
6
// ERROR
const expression = 'funky';
const raw = String.raw`A tagged template ${expression} string`;
// …
template: '<div>' + raw + '</div>';
// …

String.raw() — это теговая функция, встроенная в JavaScript ES2015.

Компилятор AOT не поддерживает выражения шаблонов с метками; избегайте их в выражениях метаданных.

Ожидается ссылка на символ

Компилятор ожидал получить ссылку на символ в месте, указанном в сообщении об ошибке.

Эта ошибка может возникнуть, если вы используете выражение в предложении extends класса.

Ссылки

Комментарии