[英]Is there a way to add a type assertion / annotation to a template input variable?
我有一個看起來像這樣的模板(我正在使用一些組件,它使用它作為重復項的基礎,它是<p-pickList>
,但問題不是關於該組件的具體問題,只是作為一個例子)
對於背景,假設我有一個類型Foo
並且我的組件有一個foos: Foo[]
,我將它提供給[source]
屬性下的<p-pickList>
組件,並為它執行內部*ngFor
,我所要做的就是提供一個模板
<ng-template let-foo pTemplate="item">
...
{{ foo.anythingGoesHereWithNoWarningAndNoAutocomplete }}
但是, foo
上的類型信息似乎丟失了。
我是類型安全的忠實粉絲,我喜歡 Intellij(或任何其他編輯器)可以向我顯示警告,如果在模板中我做了一些諸如指定foo
的無效屬性之類的事情
如果我有一個常規的*ngFor
,它會推斷出foo
的類型
<div *ngFor="let foo of foos">
{{ foo.autoCompleteWorksInMostIDEsAsWellAsWarningIfInvalidProp }}
是否有任何語法可以讓我暗示let-foo
的類型? (希望大多數 IDE 都能識別)。
如果我不想依賴 IDE,有沒有辦法讓 ng 編譯器類型檢查foo
(由 let-foo 聲明)?
tl;dr 是否有一種語法可以讓我輸入注釋模板輸入變量? 例如這樣的東西組成了語法?
let-foo="$implicit as Foo"
還是let-foo-type="Foo"
?
一個愚蠢的想法是在我的組件中有一個標識函數,例如
identity(foo: Foo): Foo {
return foo;
}
但是做
{{ identity(foo).fooProp }}
是不是很大的改進
{{ (foo as Foo).fooProp }}`
讓我們看看 angular 有什么相似之處以及它是如何工作的!
<p *ngFor="let number of [{v: 101},{v: 102}, {v: 103}]">{{number.v}}</p>
我們可以不用*
魔法來重寫它
<ng-template ngFor let-number [ngForOf]="[{v: 101},{v: 102}, {v: 103}]">
<p>{{number.v}}</p>
</ng-template>
如果沒有觀察者( ngDoCheck
),它可以與(但ngTemplateOutlet
沒有ngDoCheck
)相同:
<ng-template let-number #templateRef>
<p>{{number.v}}</p>
</ng-template>
<ng-container *ngTemplateOutlet="templateRef; context: {$implicit: {v: 101}}"></ng-container>
<ng-container *ngTemplateOutlet="templateRef; context: {$implicit: {v: 102}}"></ng-container>
<ng-container *ngTemplateOutlet="templateRef; context: {$implicit: {v: 103}}"></ng-container>
或者我們可以自己創建
// template
<button (click)=create(templateRef)>create</button>
// TS
constructor(private _viewContainerRef: ViewContainerRef) { ... }
create(templateRef: TemplateRef<{$implicit: {v: number;}}>) {
this._viewContainerRef.createEmbeddedView(templateRef, {$implicit: {v: 101}});
this._viewContainerRef.createEmbeddedView(templateRef, {$implicit: {v: 102}});
this._viewContainerRef.createEmbeddedView(templateRef, {$implicit: {v: 103}});
}
TL; 博士
模板類型檢查魔術發生在viewContainerRef.createEmbeddedView
。 (例如ngFor
); 但它假設 templateRef 接受什么。
Angular 可以在AOT
編譯:
<p *ngFor="let num of [{v:1}, {v:2}]">
{{num.does.not.exist.completly}}
</p>
因此,據我所知,我們應該假設模板具有哪些類型,但在實例化模板時進行檢查(通過createEmbeddedView
);
問題是沒有任何類型信息。 <ng-template let-foo pTemplate="item">
- 只是模板聲明,它尚未綁定到任何類型/上下文,只是一些具有或可能具有anythingGoesHereWithNoWarningAndNoAutocomplete
字段的動態類型。 如果沒有適當的泛型支持,就不可能有這種類型安全。
發現這個問題,並提出解決辦法,允許模板輸入變量來發布類型信息,這樣的IDE可以在github上提供更好的完成在這里。 此功能目前正在開發中,希望它會在 Angular 的未來版本中推出。 您現在應該在 VS Code 或 Sublime Text 中使用Angular Language Service擴展。 您可以放心擴展的支持,因為它正在由 Angular 團隊開發。 有關更多信息,請查看此自述文件和文檔。
正如您提到的,它可以通過使用身份功能來實現,但它會導致嚴重的性能問題,並且為不同類型維護此功能會很麻煩。
這可以通過將變量包裝在另一個ng-template
來解決
這是另一種解決方法,但我比這里的其他解決方案更喜歡它,因為它只是在 HTML 中添加了 2 行代碼,當然,如果您僅使用變量 1 或 2 次,則其他解決方案更好。 我的答案:
取而代之的是:
<p *ngTemplateOutlet="foo; context: {$implicit: {fooProp: 'Hello!'}}"></p>
<ng-template #foo let-args>
This is untyped: {{ args.fooProp }}<br>
</ng-template>
做這個:
<p *ngTemplateOutlet="foo; context: {$implicit: {fooProp: 'Hello!'}}"></p>
<ng-template #foo let-untypedArgs>
<ng-template [ngIf]="identity(untypedArgs)" let-args="ngIf">
This is typed: {{ args.fooProp }}<br>
</ng-template>
</ng-template>
identity(foo: Foo): Foo {
return foo;
}
正如每個人已經說過的那樣,當使用*ngFor
時,IDE 會注意到類型斷言。 它也適用於使用*ngIf
。 當然,此解決方案有一個缺點,因為內部<ng-template>
由於[ngIf]
稍后呈現。
有了這個,現在如果你在你的上下文中添加一個無效的屬性,你會得到以下編譯錯誤,這很好, 這是一個 stackblitz 演示:
屬性 'newFooProp' 在類型 'Foo' 上不存在。
我測試了這個解決方案並使用 vscode 在 Angular 11 上工作, angularCompilerOptions
屬性enableIvy
和fullTemplateTypeCheck
設置為true
。
此外,應該安裝Angular 語言服務,即使安裝了ALS ,也需要此解決方案,因為可以從任何地方調用模板,因此, ALS無法知道傳遞的上下文參數。 這可以在我添加的stackblitz 演示中輕松測試,只需將fooProp
重命名為fooProp1
,您將看到無類型示例顯示一個空值,而有類型的示例會引發錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.