Angular AOT元數(shù)據(jù)錯誤

2022-07-15 09:40 更新

AOT 元數(shù)據(jù)錯誤

以下是你可能會遇到的元數(shù)據(jù)錯誤,帶有解釋和建議的更正。

不支持表達形式 (Expression form not supported)

編譯器在對 Angular 元數(shù)據(jù)求值時遇到了一個它不能理解的表達式。

如以下范例所示,使用了編譯器的受限表達式語法之外的語言特性可能會產(chǎn)生此錯誤:

// 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 元數(shù)據(jù)的表達式中使用。

通過在編寫 Angular 元數(shù)據(jù)時堅持使用編譯器的受限表達式語法來避免此錯誤,并小心新的或不常用的 TypeScript 功能。

引用本地(未導出的)符號 (Reference to a local (non-exported) symbol)

如果要引用局部(未導出的)符號 'symbol name',請考慮導出它。

編譯器遇到了局部定義的未導出或未初始化的符號。

下面就是存在該問題的 ?provider ?范例。

// ERROR
let foo: number; // neither exported nor initialized

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

編譯器會生成這個組件工廠,其中包含 ?useValue ?提供者的代碼。那個工廠模塊不能訪問這個源碼模塊,無法訪問這個(未導出的)?foo ?變量。

你可以通過初始化 ?foo ?來修正這個錯誤。

let foo = 42; // initialized

編譯器會將表達式折疊到提供者中,就像你自己寫的一樣。

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

另外,你也可以通過導出 ?foo ?來解決它,這樣 ?foo ?將會在運行期間你真正知道它的值的時候被賦值。

// CORRECTED
export let foo: number; // exported

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

添加 ?export ?的方式通常用于需要在元數(shù)據(jù)中引用變量時,如 ?providers ?和 ?animations?,這樣編譯器就可以在這些表達式中生成對已導出變量的引用了。它不需要知道這些變量的。

當編譯器需要知道真正的值以生成代碼時,添加 ?export ?的方式就是無效的。比如這里的 ?template ?屬性。

// ERROR
export let someTemplate: string; // exported but not initialized

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

編譯器現(xiàn)在就需要 ?template ?屬性的值來生成組件工廠。 僅僅有對該變量的引用是不夠的。 給這個聲明加上 ?export ?前綴只會生成一個新的錯誤 "?Only initialized variables and constants can be referenced?【只能引用初始化過的變量和常量】"。

只支持初始化過的變量和常量 (Only initialized variables and constants)

只能引用已初始化過的變量和常量,因為模板編譯器需要該變量的值。

編譯器發(fā)現(xiàn)某個到已導出的變量或靜態(tài)字段的引用是沒有初始化過的。而它需要根據(jù)那個變量的值來生成代碼。

下面的例子試圖把組件的 ?template ?屬性設置為已導出的 ?someTemplate ?變量的值,而這個值雖然聲明過,卻沒有初始化過。

// ERROR
export let someTemplate: string;

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

如果你從其它模塊中導入了 ?someTemplate?,但那個模塊中忘了初始化它,就會看到這個錯誤。

// ERROR - not initialized there either
import { someTemplate } from './config';

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

編譯器不能等到運行時才得到該模板的信息。它必須從源碼中靜態(tài)獲得這個 ?someTemplate ?變量的值,以便生成組件工廠,組件工廠中需要包含根據(jù)這個模板來生成元素的代碼。

要糾正這個錯誤,請在同一行的初始化子句中初始化這個變量的值。

// CORRECTED
export let someTemplate = '<h1>Greetings from Angular</h1>';

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

引用未導出過的類 (Reference to a non-exported class)

對非導出類 ?<class name>? 的引用。 考慮導出此類。

元數(shù)據(jù)引用了一個未導出的類。

比如,你可能定義了一個類并在某個 ?providers ?數(shù)組中把它用作了依賴注入令牌,但是忘了導出這個類。

// ERROR
abstract class MyStrategy { }

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

Angular 在單獨的模塊中生成類工廠,并且該工廠只能訪問導出的類。要更正此錯誤,請導出所引用的類。

// CORRECTED
export abstract class MyStrategy { }

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

引用未導出過的函數(shù) (Reference to a non-exported function)

元數(shù)據(jù)中引用了未導出的函數(shù)。

比如,你可能已經(jīng)把某個服務提供者的 ?useFactory ?屬性設置成了一個局部定義但忘了導出的函數(shù)。

// ERROR
function myStrategy() { … }

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

Angular 在單獨的模塊中生成類工廠,該工廠只能訪問導出的函數(shù)。要更正此錯誤,請導出此函數(shù)。

// CORRECTED
export function myStrategy() { … }

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

不支持函數(shù)調(diào)用 (Function calls are not supported)

不支持函數(shù)調(diào)用。考慮把這個函數(shù)或 lambda 表達式替換成一個對已導出函數(shù)的引用。

編譯器當前不支持函數(shù)表達式或 lambda 函數(shù)。比如,你不能將提供者的 ?useFactory ?設置為這樣的匿名函數(shù)或箭頭函數(shù)。

// ERROR
  …
  providers: [
    { provide: MyStrategy, useFactory: function() { … } },
    { provide: OtherStrategy, useFactory: () => { … } }
  ]
  …

如果你在某個提供者的 ?useValue ?中調(diào)用函數(shù)或方法,也會導致這個錯誤。

// ERROR
import { calculateValue } from './utilities';

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

要改正這個問題,就要從模塊中導出這個函數(shù),并改成在服務提供者的 ?useFactory ?中引用該函數(shù)。

// 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 }
  ]
  …

不支持解構變量或常量 (Destructured variable or constant not supported)

模板編譯器不支持引用導出的解構語法的變量或常量。考慮簡化這一點,以避免解構語法。

編譯器不支持引用通過解構賦值的方式得到的變量。

比如,你不能這么寫:

// ERROR
import { configuration } from './configuration';

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

要糾正這個錯誤,就要引用非解構方式的變量。

// CORRECTED
import { configuration } from './configuration';
  …
  providers: [
    {provide: Foo, useValue: configuration.foo},
    {provide: Bar, useValue: configuration.bar},
  ]
  …

無法解析類型 (Could not resolve type)

編譯器遇到了某個類型,但是不知道它是由哪個模塊導出的。

這通常會發(fā)生在你引用環(huán)境類型時。比如,?Window ?類型就是在全局 ?.d.ts? 文件中聲明的環(huán)境類型。

如果你在組件的構造函數(shù)中引用它就會導致一個錯誤,因為編譯器必須對構造函數(shù)進行靜態(tài)分析。

// ERROR
@Component({ })
export class MyComponent {
  constructor (private win: Window) { … }
}

TypeScript 能理解這些環(huán)境類型,所以你不用導入它們。但 Angular 編譯器不理解你沒有導入或?qū)С鲞^的類型。

這種情況下,編譯器就無法理解如何使用這個 ?Window ?令牌來進行注入。

不要在元數(shù)據(jù)表達式中引用環(huán)境類型。

如果你必須注入某個環(huán)境類型的實例,可以用以下四步來巧妙解決這個問題:

  1. 為環(huán)境類型的實例創(chuàng)建一個注入令牌。
  2. 創(chuàng)建一個返回該實例的工廠函數(shù)。
  3. 使用該工廠函數(shù)添加一個 ?useFactory ?提供者。
  4. 使用 ?@Inject? 來注入這個實例。

下面的例子說明了這一點。

// 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) { … }
}

對于編譯器來說,構造函數(shù)中出現(xiàn) ?Window? 類型已不再是個問題,因為它現(xiàn)在使用 ?@Inject(WINDOW)? 來生成注入代碼。

Angular 也用 ?DOCUMENT ?令牌做了類似的事情,所以你也可以注入瀏覽器的 ?document ?對象(或它的一個抽象層,取決于該應用運行在哪個平臺)。

import { Inject }   from '@angular/core';
import { DOCUMENT } from '@angular/common';

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

期望的名字 (Name expected)

編譯器在正在計算的表達式中期望有一個名字。

如果將數(shù)字用作屬性名稱,則可能發(fā)生這種情況,如以下范例所示。

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

把該屬性的名字改為非數(shù)字類型。

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

不支持的枚舉成員名稱 (Unsupported enum member name)

Angular 不能確定你在元數(shù)據(jù)中引用的枚舉成員的值。

編譯器可以理解簡單的枚舉值,但不能理解復雜的,比如從那些計算屬性中派生出來的。

// 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
  ]
  …

避免引用那些使用了復雜初始化對象或計算屬性的枚舉。

不支持帶標簽的模板表達式 (Tagged template expressions are not supported)

元數(shù)據(jù)中不支持帶標簽函數(shù)的模板表達式。

編譯器遇到了 JavaScript ES2015 帶標簽的模板表達式,如下所示。

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

String.raw()是 JavaScript ES2015 的原生標簽函數(shù)。

AOT 編譯器不支持帶標簽函數(shù)的模板表達式,避免在元數(shù)據(jù)表達式中使用它們。

期待符號的引用 (Symbol reference expected)

編譯器期待在錯誤信息指出的位置是一個符號引用。

當你在類的 ?extends ?子句中使用表達式時就會出現(xiàn)這個錯誤。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號