TypeScript中能否将对象类型作为参数传递来封装通用Grid组件?
方案说明
TypeScript完全可以实现该需求,核心通过泛型组件配合动态依赖传递的方式就能完成Project-Grid桥接层的开发,把所有通用逻辑收敛到桥接层,业务侧仅需要传入对应场景的类型和依赖即可,不需要重复编写逻辑。
第一步:抽离公共类型约束
首先把三个可变依赖的公共结构抽成接口,作为泛型的约束条件,保证类型安全:
// 所有业务GridService需要实现的公共接口 interface IGridService { // 填入所有业务服务共有的方法签名,示例如下: fetchList?: (params: any) => Promise<any[]> saveData?: (data: any) => Promise<void> } // 所有Excel模板类的公共约束 interface IGridExcel { [key: string]: any } // 所有Grid入参Dto的公共约束 interface IGridDto { [key: string]: any }
第二步:开发泛型桥接组件ProjectGrid
以下分别提供Angular(根据你提供的构造函数注入语法判断大概率是Angular项目)和React两种主流框架的实现:
Angular版本
首先定义注入Token用来接收业务侧传入的动态类:
import { InjectionToken, Inject, Component } from '@angular/core' import { BaseGridComponent } from './base-grid.component' // 你的基础Grid组件 import { ExcelService, CommonService } from './common.service' // 公共服务 export const GRID_SERVICE = new InjectionToken<IGridService>('GRID_SERVICE') // 接收构造函数类型 export const GRID_EXCEL_CLASS = new InjectionToken<new () => IGridExcel>('GRID_EXCEL_CLASS') export const GRID_DTO_CLASS = new InjectionToken<new () => IGridDto>('GRID_DTO_CLASS')
实现泛型桥接组件:
@Component({ selector: 'project-grid', template: `<base-grid [config]="gridConfig" (fileChange)="onFileChange($event)"></base-grid>`, standalone: true, imports: [BaseGridComponent] }) export class ProjectGridComponent< TService extends IGridService, TExcel extends IGridExcel, TDto extends IGridDto > { gridConfig = {} // 你的Grid通用配置 constructor( private excelService: ExcelService, private commonService: CommonService, @Inject(GRID_SERVICE) public gridService: TService, @Inject(GRID_EXCEL_CLASS) private ExcelCls: new () => TExcel, @Inject(GRID_DTO_CLASS) private DtoCls: new () => TDto, ) {} // 所有业务Grid通用的逻辑全部收敛到这里,不需要重复编写 onFileChange(evt: any) { /* 通用逻辑 */ const header: string[] = Object.getOwnPropertyNames(new this.ExcelCls()) /* 剩余通用逻辑 */ } initialiseGrid(data: TDto) { /* 通用初始化逻辑 */ } }
React版本
如果是React项目可以直接通过props传递动态依赖,更简洁:
import BaseGrid from './base-grid' import { ExcelService, CommonService } from './common.service' type ProjectGridProps<TService, TExcel, TDto> = { gridService: TService ExcelCls: new () => TExcel DtoCls: new () => TDto } export function ProjectGrid< TService extends IGridService, TExcel extends IGridExcel, TDto extends IGridDto >(props: ProjectGridProps<TService, TExcel, TDto>) { const { gridService, ExcelCls, DtoCls } = props const onFileChange = (evt: any) => { /* 通用逻辑 */ const header: string[] = Object.getOwnPropertyNames(new ExcelCls()) /* 剩余通用逻辑 */ } const initialiseGrid = (data: TDto) => { /* 通用初始化逻辑 */ } return <BaseGrid onFileChange={onFileChange} /* 其他配置传递 */ /> }
第三步:业务侧使用
业务场景的Grid组件只需要传入对应场景的三个类即可,不需要写任何重复逻辑:
Angular场景GridA示例
import { Component } from '@angular/core' import { ProjectGridComponent, GRID_SERVICE, GRID_EXCEL_CLASS, GRID_DTO_CLASS } from './project-grid.component' import { GridAService } from './grid-a.service' import { GridAExcel } from './grid-a.excel' import { GridADto } from './grid-a.dto' @Component({ selector: 'grid-a', template: `<project-grid></project-grid>`, standalone: true, imports: [ProjectGridComponent], providers: [ { provide: GRID_SERVICE, useClass: GridAService }, { provide: GRID_EXCEL_CLASS, useValue: GridAExcel }, { provide: GRID_DTO_CLASS, useValue: GridADto } ] }) export class GridAComponent {}
React场景GridA示例
import { ProjectGrid } from './project-grid' import { GridAService } from './grid-a.service' import { GridAExcel } from './grid-a.excel' import { GridADto } from './grid-a.dto' export function GridA() { return <ProjectGrid gridService={new GridAService()} ExcelCls={GridAExcel} DtoCls={GridADto} /> }
方案优势
- 逻辑仅需维护一份,后续迭代修改只需要调整
ProjectGrid即可,不需要同步修改所有业务Grid - 完全类型安全,TS会自动推导所有传入依赖的类型,不会丢失类型提示和校验
- 新增业务Grid仅需要传入三个对应类,开发效率可以提升80%以上
内容的提问来源于stack exchange,提问作者rcs
相关产品推荐
相关产品推荐

