簡體   English   中英

有沒有辦法向模板輸入變量添加類型斷言/注釋?

[英]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 }}

問題:

  1. 是否有任何語法可以讓我暗示let-foo的類型? (希望大多數 IDE 都能識別)。

  2. 如果我不想依賴 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 }}`

使用接口可以實現您提到的行為:

在ts文件中,定義一個接口:

export interface Dog {
  name: string;
  age: number;
}

在組件中定義變量:

@Component()...{
...
  mrSnuggles: Dog = {
    name: 'mrSnuggles',
    age: 10
  };
}

並在模板中:

  {{mrSnuggles.food}}

你可以看到錯誤(intellij idea):

在此輸入圖像描述

讓我們看看 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 團隊開發。 有關更多信息,請查看此自述文件文檔

正如您提到的,它可以通過使用身份功能來實現,但它會導致嚴重的性能問題,並且為不同類型維護此功能會很麻煩。

VSCode 應該具有開箱即用的功能,剛剛在 stackblitz 中進行了測試: https ://stackblitz.com/edit/angular-type-assertion ? file = src/app/app.component.html

在此處輸入圖片說明

這可以通過將變量包裝在另一個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屬性enableIvyfullTemplateTypeCheck設置為true

此外,應該安裝Angular 語言服務,即使安裝了ALS ,也需要此解決方案,因為可以從任何地方調用模板,因此, ALS無法知道傳遞的上下文參數。 這可以在我添加的stackblitz 演示中輕松測試,只需將fooProp重命名為fooProp1 ,您將看到無類型示例顯示一個空值,而有類型的示例會引發錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM