正如 TypeScript 在代碼中捕獲類(lèi)型錯(cuò)誤一樣,Angular 也會(huì)檢查應(yīng)用程序模板中的表達(dá)式和綁定,并可以報(bào)告所發(fā)現(xiàn)的任何類(lèi)型錯(cuò)誤。Angular 當(dāng)前有三種執(zhí)行此操作的模式,具體取決于 TypeScript 配置文件 中的 ?fullTemplateTypeCheck
?和 ?strictTemplates
?標(biāo)志的值。
在最基本的類(lèi)型檢查模式下,將 ?fullTemplateTypeCheck
?標(biāo)志設(shè)置為 ?false
?,Angular 僅驗(yàn)證模板中的頂層表達(dá)式。
如果編寫(xiě) ?<map [city]="user.address.city">
?,則編譯器將驗(yàn)證以下內(nèi)容:
user
?是該組件類(lèi)的屬性user
?是具有 ?address
?屬性的對(duì)象user.address
? 是具有 ?city
?屬性的對(duì)象編譯器不會(huì)驗(yàn)證 ?user.address.city
? 的值是否可賦值給 ?<map>
? 組件的輸入屬性 ?city
?。
編譯器在此模式下也有一些主要限制:
*ngIf
?,?*ngFor
? 和其它 ?<ng-template>
? 嵌入式視圖。#refs
? 的類(lèi)型、管道的結(jié)果、事件綁定中 ?$event
? 的類(lèi)型等等。在許多情況下,這些東西最終都以 ?any
?類(lèi)型結(jié)束,這可能導(dǎo)致表達(dá)式的后續(xù)部分不受檢查。
如果將 ?fullTemplateTypeCheck
?標(biāo)志設(shè)置為 ?true
?,則 Angular 在模板中進(jìn)行類(lèi)型檢查時(shí)會(huì)更加主動(dòng)。特別是:
*ngIf
? 或 ?*ngFor
? 內(nèi)的 ?*ngFor
?)any
?)以下仍然具有 ?any
?類(lèi)型。
$event
? 對(duì)象?
fullTemplateTypeCheck
?標(biāo)志已經(jīng)在 Angular 13 中棄用了。它被編譯器選項(xiàng)中的 ?strictTemplates
?家族代替了。
Angular 延續(xù)了 ?fullTemplateTypeCheck
?標(biāo)志的行為,并引入了第三個(gè)“嚴(yán)格模式”。嚴(yán)格模式是完全模式的超集,可以通過(guò)將 ?strictTemplates
?標(biāo)志設(shè)置為 true 來(lái)訪問(wèn)。該標(biāo)志取代 ?fullTemplateTypeCheck
?標(biāo)志。在嚴(yán)格模式下,Angular 添加了超出 8 版類(lèi)型檢查器的檢查。
注意:
嚴(yán)格模式僅在使用 Ivy 時(shí)可用。
除了完全模式的行為之外,Angular 版本 9 還會(huì):
@Input()
?strictNullChecks
?標(biāo)志NgFor
?進(jìn)行正確的類(lèi)型檢查)$event
? 的正確類(lèi)型document.createElement
? 將為該標(biāo)簽返回正確的類(lèi)型),推斷出對(duì) DOM 元素的局部引用的正確類(lèi)型類(lèi)型檢查的三種模式對(duì)嵌入式視圖的處理方式不同??紤]以下范例。
interface User {
name: string;
address: {
city: string;
state: string;
}
}
<div *ngFor="let user of users">
<h2>{{config.title}}</h2>
<span>City: {{user.address.city}}</span>
</div>
?<h2>
? 和 ?<span>
? 在 ?*ngFor
? 嵌入式視圖中。在基本模式下,Angular 不會(huì)檢查它們中的任何一個(gè)。但是,在完全模式下,Angular 會(huì)檢查 ?config
?和 ?user
?是否存在,并假設(shè)為 ?any
?的類(lèi)型。在嚴(yán)格模式下,Angular 知道該 ?user
?在 ?<span>
? 中是 ?User
?類(lèi)型,而 ?address
?是與一個(gè)對(duì)象,它有一個(gè) ?string
?類(lèi)型的屬性 ?city
?。
使用嚴(yán)格模式,你可能會(huì)遇到在以前的兩種模式下都沒(méi)有出現(xiàn)過(guò)的模板錯(cuò)誤。這些錯(cuò)誤通常表示模板中的真正類(lèi)型不匹配,而以前的工具并未捕獲這些錯(cuò)誤。在這種情況下,該錯(cuò)誤消息會(huì)使該問(wèn)題在模板中的位置清晰可見(jiàn)。
當(dāng) Angular 庫(kù)的類(lèi)型不完整或不正確,或者在以下情況下類(lèi)型與預(yù)期不完全一致時(shí),也可能存在誤報(bào)。
strictNullChecks
?,則可能缺少 ?null | undefined
?)<input disabled>
?。$event.target
? 用于 DOM 事件時(shí)(由于事件冒泡的可能性,DOM 類(lèi)型中的 ?$event.target
? 不具有你可能期望的類(lèi)型)如果發(fā)生此類(lèi)誤報(bào),則有以下幾種選擇:
$any()
? 類(lèi)型轉(zhuǎn)換函數(shù)可以選擇不對(duì)部分表達(dá)式進(jìn)行類(lèi)型檢查tsconfig.json
? 中設(shè)置 ?strictTemplates: false
? 來(lái)完全禁用嚴(yán)格檢查false
?,可以在保持其它方面的嚴(yán)格性的同時(shí),單獨(dú)禁用某些特定的類(lèi)型檢查操作strictTemplates
?和 ?strictNullChecks
?,則可以通過(guò) ?strictNullInputTypes
?來(lái)選擇性排除專(zhuān)門(mén)用于輸入綁定的嚴(yán)格空類(lèi)型檢查除非另行說(shuō)明,下面的每個(gè)選項(xiàng)都會(huì)設(shè)置為 ?strictTemplates
?的值(當(dāng) ?strictTemplates
?為真時(shí)是 ?true
?,其他值也一樣)。
嚴(yán)格標(biāo)志 |
影響 |
---|---|
strictInputTypes
|
是否檢查綁定表達(dá)式對(duì) `@Input()` 字段的可賦值性。也會(huì)影響指令泛型類(lèi)型的推斷。 |
strictInputAccessModifiers
|
在把綁定表達(dá)式賦值給 `@Input()` 時(shí),是否檢查像 `private`/`protected`/`readonly` 這樣的訪問(wèn)修飾符。如果禁用,則 `@Input` 上的訪問(wèn)修飾符會(huì)被忽略,只進(jìn)行類(lèi)型檢查。本選項(xiàng)默認(rèn)為 `false`,即使當(dāng) `strictTemplates` 為 `true` 時(shí)也一樣。 |
strictNullInputTypes
|
檢查 `@Input()` 綁定時(shí)是否要 `strictNullChecks`(對(duì)于每個(gè) `strictInputTypes`)。當(dāng)使用的庫(kù)不是基于 `strictNullChecks` 構(gòu)建的時(shí),將其關(guān)閉會(huì)很有幫助。 |
strictAttributeTypes
|
是否檢查使用文本屬性進(jìn)行的 (將 disabled 屬性設(shè)置為字符串 'true' )
disabled 屬性設(shè)置為布爾值 true )。 |
strictSafeNavigationTypes
|
是否根據(jù) `user` 的類(lèi)型正確推斷出安全導(dǎo)航操作的返回類(lèi)型(比如 `user?.name`)。如果禁用,則 `user?.name` 的類(lèi)型為 `any`。 |
strictDomLocalRefTypes
|
對(duì) DOM 元素的本地引用是否將具有正確的類(lèi)型。如果禁用,對(duì)于 `` 來(lái)說(shuō) `ref` 會(huì)是 `any` 類(lèi)型的。 |
strictOutputEventTypes
|
對(duì)于綁定到組件/指令 `@Output()` 或動(dòng)畫(huà)事件的事件綁定,`$event` 是否具有正確的類(lèi)型。如果禁用,它將為 `any`。 |
strictDomEventTypes
|
對(duì)于與 DOM 事件的事件綁定,`$event` 是否具有正確的類(lèi)型。如果禁用,它將為 `any`。 |
strictContextGenerics
|
泛型組件的類(lèi)型參數(shù)是否應(yīng)該被正確推斷(包括泛型上界和下界). 如果禁用它,所有的類(lèi)型參數(shù)都會(huì)被當(dāng)做 `any`。 |
strictLiteralTypes
|
是否要推斷模板中聲明的對(duì)象和數(shù)組字面量的類(lèi)型。如果禁用,則此類(lèi)文字的類(lèi)型就是 `any`。當(dāng) `fullTemplateTypeCheck` 或 `strictTemplates` 為 `true` 時(shí),此標(biāo)志為 `true`。 |
如果使用這些標(biāo)志進(jìn)行故障排除后仍然存在問(wèn)題,可以通過(guò)禁用 ?strictTemplates
?退回到完全模式。
如果這不起作用,則最后一種選擇是完全關(guān)閉 full 模式,并使用 ?fullTemplateTypeCheck: false
?,因?yàn)樵谶@種情況下,我們已經(jīng)做了一些特殊的努力來(lái)使 Angular 9 向后兼容。
你無(wú)法使用任何推薦方式解決的類(lèi)型檢查錯(cuò)誤可能是因?yàn)槟0孱?lèi)型檢查器本身存在錯(cuò)誤。如果遇到需要退回到基本模式的錯(cuò)誤,則很可能是這樣的錯(cuò)誤。如果發(fā)生這種情況,請(qǐng)提出問(wèn)題,以便開(kāi)發(fā)組解決。
模板類(lèi)型檢查器會(huì)檢查綁定表達(dá)式的類(lèi)型是否與相應(yīng)指令輸入的類(lèi)型兼容。比如,請(qǐng)考慮以下組件:
export interface User {
name: string;
}
@Component({
selector: 'user-detail',
template: '{{ user.name }}',
})
export class UserDetailComponent {
@Input() user: User;
}
?AppComponent
?模板按以下方式使用此組件:
@Component({
selector: 'app-root',
template: '<user-detail [user]="selectedUser"></user-detail>',
})
export class AppComponent {
selectedUser: User | null = null;
}
這里,在檢查 ?AppComponent
?的模板期間,?[user]="selectedUser"
? 綁定與 ?UserDetailComponent.user
? 輸入屬性相對(duì)應(yīng)。因此,Angular 會(huì)將 ?selectedUser
?屬性賦值給 ?UserDetailComponent.user
?,如果它們的類(lèi)型不兼容,則將導(dǎo)致錯(cuò)誤。TypeScript 會(huì)根據(jù)其類(lèi)型系統(tǒng)進(jìn)行賦值檢查,并遵循在應(yīng)用程序中配置的標(biāo)志(比如 ?strictNullChecks
?)。
通過(guò)向模板類(lèi)型檢查器提出更具體的模板內(nèi)類(lèi)型要求,可以避免一些運(yùn)行時(shí)類(lèi)型錯(cuò)誤。通過(guò)在指令定義中提供各種“模板守衛(wèi)”功能,可以讓自定義指令的輸入類(lèi)型要求盡可能具體。
當(dāng)你啟用 ?strictTemplates
?和 TypeScript 標(biāo)志 ?strictNullChecks
?,在某些情況下可能會(huì)發(fā)生類(lèi)型檢查錯(cuò)誤,這些情況很難避免。比如:
strictNullChecks
?的庫(kù)中的指令。對(duì)于沒(méi)有使用 ?strictNullChecks
?編譯的庫(kù),其聲明文件將不會(huì)指示字段是否可以為 ?null
?。對(duì)于庫(kù)正確處理 ?null
?的情況,這是有問(wèn)題的,因?yàn)榫幾g器將根據(jù)聲明文件進(jìn)行空值檢查,而它省略了 ?null
?類(lèi)型。這樣,編譯器會(huì)產(chǎn)生類(lèi)型檢查錯(cuò)誤,因?yàn)樗袷?nbsp;?strictNullChecks
?。
async
?管道與 Observable 一起使用會(huì)同步發(fā)出值。?async
?管道當(dāng)前假定它預(yù)訂的 Observable 可以是異步的,這意味著可能還沒(méi)有可用的值。在這種情況下,它仍然必須返回某些內(nèi)容 —— ?null
?。換句話說(shuō),?async
?管道的返回類(lèi)型包括 ?null
?,這在知道此 Observable 會(huì)同步發(fā)出非空值的情況下可能會(huì)導(dǎo)致錯(cuò)誤。
對(duì)于上述問(wèn)題,有兩種潛在的解決方法:
!
? 用在可為空的表達(dá)式的末尾,比如<user-detail [user]="user!"></user-detail>
在此范例中,編譯器在可空性方面會(huì)忽略類(lèi)型不兼容,就像在 TypeScript 代碼中一樣。對(duì)于 ?async
?管道,請(qǐng)注意,表達(dá)式需要用括號(hào)括起來(lái),如
<user-detail [user]="(user$ | async)!"></user-detail>
當(dāng)啟用 ?strictTemplates
?時(shí),仍然可以禁用類(lèi)型檢查的某些方面。將選項(xiàng) ?strictNullInputTypes
?設(shè)置為 ?false
?將禁用 Angular 模板中的嚴(yán)格空檢查。此標(biāo)志會(huì)作用于應(yīng)用程序中包含的所有組件。
作為庫(kù)作者,你可以采取多種措施為用戶提供最佳體驗(yàn)。首先,啟用 ?strictNullChecks
?并在輸入的類(lèi)型中包括 ?null
?(如果適用),可以與消費(fèi)者溝通,看他們是否可以提供可空的值。
有時(shí),指令或組件的 ?@Input()
?最好更改綁定到它的值,通常使用此輸入的 getter / setter 對(duì)。比如,考慮以下自定義按鈕組件:
考慮以下指令:
@Component({
selector: 'submit-button',
template: `
<div class="wrapper">
<button [disabled]="disabled">Submit</button>
</div>
`,
})
class SubmitButton {
private _disabled: boolean;
@Input()
get disabled(): boolean {
return this._disabled;
}
set disabled(value: boolean) {
this._disabled = value;
}
}
在這里,組件的輸入 ?disabled
?將傳給模板中的 ?<button>
?。只要將 ?boolean
?值綁定到輸入,所有這些工作都可以按預(yù)期進(jìn)行。但是,假設(shè)使用者使用模板中的這個(gè)輸入作為屬性:
<submit-button disabled></submit-button>
這與綁定具有相同的效果:
<submit-button [disabled]="''"></submit-button>
在運(yùn)行時(shí),輸入將設(shè)置為空字符串,這不是 ?boolean
?值。處理此問(wèn)題的角組件庫(kù)通常將值“強(qiáng)制轉(zhuǎn)換”到 setter 中的正確類(lèi)型中:
set disabled(value: boolean) {
this._disabled = (value === '') || value;
}
最好在這里將 ?value
?的類(lèi)型從 ?boolean
?更改為 ?boolean|''
? 以匹配 setter 實(shí)際會(huì)接受的一組值。TypeScript 4.3 之前的版本要求 getter 和 setter 的類(lèi)型相同,因此,如果 getter 要返回 ?boolean
?則 setter 會(huì)卡在較窄的類(lèi)型上。
如果消費(fèi)者對(duì)模板啟用了 Angular 的最嚴(yán)格的類(lèi)型檢查功能,則會(huì)產(chǎn)生一個(gè)問(wèn)題:空字符串 ?''
? 實(shí)際上無(wú)法賦值給 ?disabled
?字段,使用屬性格式寫(xiě)會(huì)產(chǎn)生類(lèi)型錯(cuò)誤。
作為解決此問(wèn)題的一種取巧方式,Angular 支持對(duì) ?@Input()
? 檢查比聲明的輸入字段更寬松的類(lèi)型。通過(guò)向組件類(lèi)添加帶有 ?ngAcceptInputType_
?前綴的靜態(tài)屬性來(lái)啟用此功能:
class SubmitButton {
private _disabled: boolean;
@Input()
get disabled(): boolean {
return this._disabled;
}
set disabled(value: boolean) {
this._disabled = (value === '') || value;
}
static ngAcceptInputType_disabled: boolean|'';
}
從 TypeScript 4.3 開(kāi)始,setter 能夠聲明為接受 ?
boolean|''
? 類(lèi)型,這就讓輸入屬性 setter 強(qiáng)制類(lèi)型轉(zhuǎn)換字段過(guò)時(shí)了。因此,輸入屬性 setter 強(qiáng)制類(lèi)型轉(zhuǎn)換字段也就棄用了。
該字段不需要值。它只要存在就會(huì)通知 Angular 的類(lèi)型檢查器,?disabled
?輸入應(yīng)被視為接受與 ?boolean|''
? 類(lèi)型匹配的綁定。后綴應(yīng)為 ?@Input
? 字段的名稱(chēng)。
請(qǐng)注意,如果給定輸入存在 ?ngAcceptInputType_
? 覆蓋,則設(shè)置器應(yīng)能夠處理任何覆蓋類(lèi)型的值。
可以通過(guò)把綁定表達(dá)式包含在類(lèi)型轉(zhuǎn)換偽函數(shù) ?$any()
? 中來(lái)禁用類(lèi)型檢查。編譯器會(huì)像在 TypeScript 中使用 ?<any>
? 或 ?as any
? 進(jìn)行類(lèi)型轉(zhuǎn)換一樣對(duì)待它。
在以下范例中,將 ?person
?強(qiáng)制轉(zhuǎn)換為 ?any
?類(lèi)型可以壓制錯(cuò)誤 ?Property address does not exist
?。
@Component({
selector: 'my-component',
template: '{{$any(person).addresss.street}}'
})
class MyComponent {
person?: Person;
}
更多建議: