有没有一种方法可以将类型断言/注释添加到模板输入变量?

时间:2018-08-29 22:59:52

标签: angular angular6

背景

我有一个看起来像这样的模板(我正在使用某个组件,将其用作重复项的基础,它是p-pickList,但是问题并不仅仅针对该组件,而是作为示例)

对于背景,假设我有一个Foo类型,而我的组件有一个foos: Foo[],我将其馈送到<p-pickList>属性下的[source]组件中,其内部*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 }}

6 个答案:

答案 0 :(得分:4)

VSCode应该具有开箱即用的功能,刚刚在stackblitz中进行了测试:https://stackblitz.com/edit/angular-type-assertion?file=src/app/app.component.html

enter image description here

答案 1 :(得分:2)

使用界面可以实现您提到的行为:

在ts文件中,定义一个接口:

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

在组件中定义变量:

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

并在模板中:

  {{mrSnuggles.food}}

您会看到错误(智能提示):

enter image description here

答案 2 :(得分:2)

让我们看看什么角度具有相似的角度以及如何工作!

<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 have no typecheck)相同:

<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; DR

模板类型检查魔术发生在viewContainerRef.createEmbeddedView内部。 (例如ngFor); 但它假设templateRef接受什么。

Angular可以在AOT中编译:

<p *ngFor="let num of [{v:1}, {v:2}]">
  {{num.does.not.exist.completly}}
</p>

因此,据我了解,我们应该假设模板具有哪些类型,但要检查模板何时实例化(由createEmbeddedView进行;

答案 3 :(得分:2)

发现了这个问题,并提出了允许模板输入变量发布类型信息的解决方案,以便IDE可以在github here上提供更好的完成度。该功能目前正在开发中,希望它将在Angular的未来版本中发布。您现在应该在VS Code或Sublime Text中使用Angular Language Service扩展名。您可以放心,因为Angular团队正在开发扩展的支持。有关更多信息,请查看此readmedocs

正如您所提到的,可以通过使用身份功能来实现,但这会导致严重的性能问题,并且为不同类型的用户维护此功能将很麻烦。

答案 4 :(得分:1)

问题是没有任何类型信息。 viewWillAppear-只是模板声明,它尚未绑定到任何类型/上下文,只是某些具有或可能具有viewDidAppear字段的动态类型。没有适当的泛型支持,就不可能拥有这种类型安全性。

答案 5 :(得分:1)

这可以通过将变量包装在另一个 ng-template

中来解决

这是另一种解决方法,但我比这里的其他解决方案更喜欢它,因为它只是在 HTML 中添加了 2 行代码,当然,如果您只使用变量 1 或 2 次 {{3 }} 更好。我的回答:

取而代之的是:

<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] 而稍后呈现。

有了这个,现在如果你向你的上下文添加一个无效的属性,你会得到以下编译错误,这很好,this other solution

<块引用>

属性 'newFooProp' 在类型 'Foo' 上不存在。

我通过阅读 here's a stackblitz demo 想出了这个解决方案:

documentation here

我测试了这个解决方案,并使用 vscode 在 Angular 11 上运行,angularCompilerOptions 属性 enableIvyfullTemplateTypeCheck 设置在 true 上。

另外,enter image description here 应该安装,即使安装了 Angular Language Service 也需要这个解决方案,因为模板可以从任何地方调用,所以 ALS 没有办法知道传递的上下文参数。这可以在我添加的 ALS 中轻松测试,只需将 fooProp 重命名为 fooProp1,您会看到无类型示例显示一个空值,而有类型的示例会抛出一个错误。