如何为ExcelScript Stub编写ExcelJS的TypeScript包装器?
如何将ExcelScript桩代码包装为ExcelJS适配层
核心思路是保留ExcelScript对外暴露的类、方法和类型定义,在内部用ExcelJS的实例实现具体逻辑,同时处理两者的API差异:同名方法做参数/返回值适配,ExcelJS缺失的方法自行补全逻辑。
步骤1:安装ExcelJS
先安装依赖包:
npm install exceljs @types/exceljs --save
步骤2:改造ExcelScript命名空间的类
每个类内部持有对应的ExcelJS实例,将ExcelScript的方法调用转发到ExcelJS,同时处理差异:
import * as exceljs from 'exceljs'; export namespace ExcelScript { export class Workbook { // 持有ExcelJS的Workbook实例 private readonly _workbook: exceljs.Workbook; constructor() { this._workbook = new exceljs.Workbook(); } getWorksheet(indexOrName: string | number): Worksheet { const jsWorksheet = this._workbook.getWorksheet(indexOrName); if (!jsWorksheet) { // 模拟桩代码行为,返回新工作表实例 const newJsSheet = this._workbook.addWorksheet(); return new Worksheet(newJsSheet); } return new Worksheet(jsWorksheet); } // 补充ExcelScript可能需要的保存方法 save(filePath: string): Promise<void> { return this._workbook.xlsx.writeFile(filePath); } } export enum RangeCopyType { values } export class Worksheet { private readonly _worksheet: exceljs.Worksheet; constructor(worksheet: exceljs.Worksheet) { this._worksheet = worksheet; } getCell(x: number, y: number): Range { // ExcelJS行列也是1-based,直接调用对应方法 const jsCell = this._worksheet.getCell(x, y); return new Range(jsCell); } getRange(x: number | string, y?: string): Range { let jsRange: exceljs.Range; if (typeof x === 'number' && y) { jsRange = this._worksheet.getRange(x, y); } else { jsRange = this._worksheet.getRange(x as string); } return new Range(jsRange); } getTables(): Table[] { return this._worksheet.tables.map(table => new Table(table)); } } export class Range { private readonly _range: exceljs.Range; constructor(range: exceljs.Range) { this._range = range; } setValue(value: string | number | boolean | Date): Range { this._range.value = value; return this; // 保持ExcelScript的链式调用特性 } copyFrom(sourceRange: Range, copyType: RangeCopyType): [][] { const options: exceljs.CopyOptions = {}; if (copyType === RangeCopyType.values) { options.valuesOnly = true; } this._range.copyFrom(sourceRange._range, options); return this._range.values as [][]; } getText(): string { return this._range.text as string; } getTexts(): [][] { return this._range.text as [][]; } getValue(): string | number | boolean | Date { return this._range.value as string | number | boolean | Date; } setNumberFormat(formatString: string): void { this._range.numFmt = formatString; } convertDataTypeToText(): void { // ExcelJS无此方法,自行实现:遍历单元格转文本格式 this._range.eachCell({ includeEmpty: true }, cell => { if (cell.value != null) { cell.value = String(cell.value); cell.numFmt = '@'; // 设置为文本格式 } }); } } export class Table { private readonly _table: exceljs.Table; constructor(table: exceljs.Table) { this._table = table; } getRange(x?: string, y?: string): Range { return new Range(this._table.range); } getTexts(): [][] { return this._table.range.text as [][]; } } }
关键适配点说明
- 实例持有:每个ExcelScript类内部都包含对应的ExcelJS实例,所有操作最终委托给这个实例执行。
- 同名方法适配:比如
getWorksheet、getCell等,直接映射到ExcelJS的同名方法,对齐参数和返回值类型。 - 缺失方法补全:像
convertDataTypeToText这类ExcelJS没有的方法,通过遍历单元格、修改值和格式的方式自行实现。 - 链式调用保持:ExcelScript的方法(如
setValue)支持链式调用,包装时注意返回当前实例。
使用示例
import { ExcelScript } from './your-wrapper-file'; async function test() { const workbook = new ExcelScript.Workbook(); const worksheet = workbook.getWorksheet('Sheet1'); // 设置单元格值并指定格式 worksheet.getCell(1, 1).setValue('Hello').setNumberFormat('@'); // 复制单元格值 const sourceRange = worksheet.getCell(1,1); worksheet.getCell(1,2).copyFrom(sourceRange, ExcelScript.RangeCopyType.values); // 保存文件 await workbook.save('./test.xlsx'); } test();
内容的提问来源于stack exchange,提问作者Arkanit
相关产品推荐
相关产品推荐

