如何为自定义Angular指令的上下文变量设置泛型类型?
Great question! The issue here is that Angular's template type inference relies on explicit type definitions for both the directive's inputs and the view context. Right now, your directive uses any everywhere, so TypeScript can't infer the correct type for the template variable. Let's fix this by making your directive generic and defining a proper context type.
Step 1: Define a Typed Context Interface
First, create an interface to explicitly type your view context. This tells TypeScript what the template's implicit variable should be typed as:
interface NgStreamContext<T> { $implicit: T; }
Step 2: Convert the Directive to a Generic Class
Update your directive to accept a generic type parameter T that aligns with the Observable's emission type. We'll replace all any types with this generic to ensure type consistency:
import { Directive, Input, OnDestroy, TemplateRef, ViewContainerRef, EmbeddedViewRef } from '@angular/core'; import { Observable, Subscription } from 'rxjs'; interface NgStreamContext<T> { $implicit: T; } export class NgStreamDirective<T> implements OnDestroy { @Input('ngStreamOf') set values(obs: Observable<T>) { this.subscription = obs.subscribe((o) => { this.value = o; if (this.embeddedRef === undefined) { this.embeddedRef = this._viewContainer.createEmbeddedView<NgStreamContext<T>>( this._template, { "$implicit": this.value } ); } else { this.embeddedRef.context.$implicit = this.value; this.embeddedRef.detectChanges(); } }); } private subscription?: Subscription; private value?: T; private embeddedRef?: EmbeddedViewRef<NgStreamContext<T>>; constructor( private _template: TemplateRef<NgStreamContext<T>>, private _viewContainer: ViewContainerRef ) {} ngOnDestroy(): void { this.subscription?.unsubscribe(); } }
Step 3: Use the Directive with Typed Observables
Now when you use your directive with a typed Observable, the template variable will automatically inherit the correct type:
<!-- stream will be inferred as string type --> <div *ngStream="let stream of observable$"> {{ stream.toUpperCase() }} <!-- TypeScript recognizes stream as string here --> </div>
Why This Works
- By making the directive generic, we tie the Observable's emission type directly to the context's
$implicitvalue. - The
TemplateRef<NgStreamContext<T>>parameter tells Angular's template type checker exactly what context the template expects, enabling proper type inference for the variable. - We added optional chaining to the unsubscribe call to avoid errors if the input was never set before component destruction.
内容的提问来源于stack exchange,提问作者Leo Letto

