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

2022-07-15 09:40 更新

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

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

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

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

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

// 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' } };
  …

你可以在普通的應(yīng)用代碼中使用 ?typeof ?和方括號(hào)標(biāo)記法來指定屬性名,但是這些特性不能在定義 Angular 元數(shù)據(jù)的表達(dá)式中使用。

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

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

如果要引用局部(未導(dǎo)出的)符號(hào) 'symbol name',請(qǐng)考慮導(dǎo)出它。

編譯器遇到了局部定義的未導(dǎo)出或未初始化的符號(hào)。

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

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

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

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

你可以通過初始化 ?foo ?來修正這個(gè)錯(cuò)誤。

let foo = 42; // initialized

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

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

另外,你也可以通過導(dǎo)出 ?foo ?來解決它,這樣 ?foo ?將會(huì)在運(yùn)行期間你真正知道它的值的時(shí)候被賦值。

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

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

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

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

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

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

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

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

只能引用已初始化過的變量和常量,因?yàn)槟0寰幾g器需要該變量的值。

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

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

// ERROR
export let someTemplate: string;

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

如果你從其它模塊中導(dǎo)入了 ?someTemplate?,但那個(gè)模塊中忘了初始化它,就會(huì)看到這個(gè)錯(cuò)誤。

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

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

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

要糾正這個(gè)錯(cuò)誤,請(qǐng)?jiān)?b>同一行的初始化子句中初始化這個(gè)變量的值。

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

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

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

對(duì)非導(dǎo)出類 ?<class name>? 的引用。 考慮導(dǎo)出此類。

元數(shù)據(jù)引用了一個(gè)未導(dǎo)出的類。

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

// ERROR
abstract class MyStrategy { }

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

Angular 在單獨(dú)的模塊中生成類工廠,并且該工廠只能訪問導(dǎo)出的類。要更正此錯(cuò)誤,請(qǐng)導(dǎo)出所引用的類。

// CORRECTED
export abstract class MyStrategy { }

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

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

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

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

// ERROR
function myStrategy() { … }

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

Angular 在單獨(dú)的模塊中生成類工廠,該工廠只能訪問導(dǎo)出的函數(shù)。要更正此錯(cuò)誤,請(qǐng)導(dǎo)出此函數(shù)。

// CORRECTED
export function myStrategy() { … }

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

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

不支持函數(shù)調(diào)用??紤]把這個(gè)函數(shù)或 lambda 表達(dá)式替換成一個(gè)對(duì)已導(dǎo)出函數(shù)的引用。

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

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

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

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

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

要改正這個(gè)問題,就要從模塊中導(dǎo)出這個(gè)函數(shù),并改成在服務(wù)提供者的 ?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 }
  ]
  …

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

模板編譯器不支持引用導(dǎo)出的解構(gòu)語(yǔ)法的變量或常量。考慮簡(jiǎn)化這一點(diǎn),以避免解構(gòu)語(yǔ)法。

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

比如,你不能這么寫:

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

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

要糾正這個(gè)錯(cuò)誤,就要引用非解構(gòu)方式的變量。

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

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

編譯器遇到了某個(gè)類型,但是不知道它是由哪個(gè)模塊導(dǎo)出的。

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

如果你在組件的構(gòu)造函數(shù)中引用它就會(huì)導(dǎo)致一個(gè)錯(cuò)誤,因?yàn)榫幾g器必須對(duì)構(gòu)造函數(shù)進(jìn)行靜態(tài)分析。

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

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

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

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

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

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

下面的例子說明了這一點(diǎn)。

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

對(duì)于編譯器來說,構(gòu)造函數(shù)中出現(xiàn) ?Window? 類型已不再是個(gè)問題,因?yàn)樗F(xiàn)在使用 ?@Inject(WINDOW)? 來生成注入代碼。

Angular 也用 ?DOCUMENT ?令牌做了類似的事情,所以你也可以注入瀏覽器的 ?document ?對(duì)象(或它的一個(gè)抽象層,取決于該應(yīng)用運(yùn)行在哪個(gè)平臺(tái))。

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

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

期望的名字 (Name expected)

編譯器在正在計(jì)算的表達(dá)式中期望有一個(gè)名字。

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

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

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

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

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

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

編譯器可以理解簡(jiǎn)單的枚舉值,但不能理解復(fù)雜的,比如從那些計(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
  ]
  …

避免引用那些使用了復(fù)雜初始化對(duì)象或計(jì)算屬性的枚舉。

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

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

編譯器遇到了 JavaScript ES2015 帶標(biāo)簽的模板表達(dá)式,如下所示。

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

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

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

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

編譯器期待在錯(cuò)誤信息指出的位置是一個(gè)符號(hào)引用。

當(dāng)你在類的 ?extends ?子句中使用表達(dá)式時(shí)就會(huì)出現(xiàn)這個(gè)錯(cuò)誤。


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)