如何在Angular ModalService中自动推断组件@Input属性类型?
Angular ModalService 自动推断@Input参数优化方案
核心思路
通过TypeScript的映射类型与条件类型,直接从组件类型中过滤出@Input属性(排除@Output及其他非输入成员),无需在组件中添加冗余的_options字段,同时通过泛型实现通用的open方法,适配所有模态组件。
实现步骤
1. 定义类型工具提取@Input属性
首先编写一个类型工具,精准筛选出组件类中的@Input属性:
import { EventEmitter } from '@angular/core'; // 判断属性是否为@Output的EventEmitter类型 type IsOutput<T> = T extends EventEmitter<any> ? true : false; // 从组件类型中排除所有@Output属性,仅保留@Input属性 type InputsOf<T> = Omit<T, { [K in keyof T]: IsOutput<T[K]> }[keyof T]>;
2. 实现通用ModalService
基于Angular CDK的Overlay实现模态框服务,利用泛型自动推断组件的@Input参数类型:
import { Injectable, Type, ComponentRef, inject } from '@angular/core'; import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; @Injectable({ providedIn: 'root' }) export class ModalService { private overlay = inject(Overlay); open<T>(component: Type<T>, options: InputsOf<T>): ComponentRef<T> { // 创建Overlay实例,配置模态框位置与遮罩 const overlayRef = this.overlay.create({ positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(), hasBackdrop: true, backdropClass: 'modal-backdrop' }); // 创建组件门户并挂载到Overlay const portal = new ComponentPortal(component); const componentRef = overlayRef.attach(portal); // 将options赋值给组件的@Input属性 Object.entries(options).forEach(([key, value]) => { componentRef.instance[key as keyof T] = value; }); // 点击遮罩关闭模态框 overlayRef.backdropClick().subscribe(() => { overlayRef.dispose(); }); return componentRef; } }
3. 编写模态组件示例
无需添加冗余字段,正常定义@Input和@Output即可:
import { Component, Input, Output, EventEmitter } from '@angular/core'; @Component({ template: ` <div class="modal-container"> <h3>确认操作</h3> <p>{{ message }}</p> <div class="modal-actions"> <button (click)="confirm.emit()" class="btn-confirm">确认</button> <button (click)="cancel.emit()" class="btn-cancel">取消</button> </div> </div> `, styles: [` .modal-container { padding: 20px; background: white; border-radius: 8px; } .modal-actions { margin-top: 20px; display: flex; gap: 10px; justify-content: flex-end; } `] }) export class ConfirmationModal { @Input() message!: string; @Output() confirm = new EventEmitter<void>(); @Output() cancel = new EventEmitter<void>(); }
4. 使用示例
调用open方法时,TypeScript会自动推断第二个参数只能包含@Input字段:
// 正确:仅传入message字段(符合@Input类型) const modalRef = this.modalService.open(ConfirmationModal, { message: '确定要删除这条数据吗?' }); // 错误:传入了@Output的confirm字段,TypeScript会报错 this.modalService.open(ConfirmationModal, { message: '提示', confirm: () => {} });
注意事项
- 如果组件中存在非
@Output但类型为EventEmitter的属性,类型工具会误判为@Output,此时可根据实际需求调整IsOutput的判断逻辑。 - 运行时赋值
@Input属性后,Angular会自动触发变更检测,无需手动调用detectChanges。
内容的提问来源于stack exchange,提问作者Mark Johansen
相关产品推荐
相关产品推荐

