我有一个看起来像这样的模板(我正在使用某个组件,将其用作重复项的基础,它是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 }}
是否有任何语法可以提示我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 }}
答案 0 :(得分:4)
VSCode应该具有开箱即用的功能,刚刚在stackblitz中进行了测试:https://stackblitz.com/edit/angular-type-assertion?file=src/app/app.component.html
答案 1 :(得分:2)
使用界面可以实现您提到的行为:
在ts文件中,定义一个接口:
export interface Dog {
name: string;
age: number;
}
在组件中定义变量:
@Component()...{
...
mrSnuggles: Dog = {
name: 'mrSnuggles',
age: 10
};
}
并在模板中:
{{mrSnuggles.food}}
您会看到错误(智能提示):
答案 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团队正在开发扩展的支持。有关更多信息,请查看此readme和docs。
正如您所提到的,可以通过使用身份功能来实现,但这会导致严重的性能问题,并且为不同类型的用户维护此功能将很麻烦。
答案 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 想出了这个解决方案:
我测试了这个解决方案,并使用 vscode 在 Angular 11 上运行,angularCompilerOptions
属性 enableIvy
和 fullTemplateTypeCheck
设置在 true
上。
另外, 应该安装,即使安装了 Angular Language Service 也需要这个解决方案,因为模板可以从任何地方调用,所以 ALS 没有办法知道传递的上下文参数。这可以在我添加的 ALS 中轻松测试,只需将
fooProp
重命名为 fooProp1
,您会看到无类型示例显示一个空值,而有类型的示例会抛出一个错误。