TypeScript中能否继承ReadonlyArray/ReadonlySet/ReadonlyMap?
实现TypeScript自定义只读行向量类(ReadonlyRowVector)
问题背景
在TypeScript中尝试直接继承ReadonlyArray会报错,因为它是接口而非类:
export class ReadonlyRowVector<ElementType> extends ReadonlyArray<ElementType> { /* ... */ }
TS2689: Cannot extend an interface
ReadonlyArray. Did you meanimplements?
若改为继承Array并实现ReadonlyArray,则会保留push/pop等可变方法,不符合只读需求;直接类型转换也会因泛型作用域问题报错。我们需要一个兼具只读特性、自定义方法,且支持instanceof校验的行向量类。
解决方案:组合模式实现
通过内部维护只读数组+实现ReadonlyArray接口的方式,既保证类型兼容,又完全控制暴露的方法,同时支持instanceof校验。
完整实现代码
import { isNaturalNumber, Logger, InvalidParameterValueError, isNumber } from "@yamato-daiwa/es-extensions"; export default class ReadonlyRowVector<ElementType> implements ReadonlyArray<ElementType> { // 内部存储只读数据 private readonly elements: ReadonlyArray<ElementType>; // 实现ReadonlyArray核心属性与方法 get length(): number { return this.elements.length; } [index: number]: ElementType; public entries(): IterableIterator<[number, ElementType]> { return this.elements.entries(); } public keys(): IterableIterator<number> { return this.elements.keys(); } public values(): IterableIterator<ElementType> { return this.elements.values(); } public forEach(callbackfn: (value: ElementType, index: number, array: ReadonlyArray<ElementType>) => void, thisArg?: any): void { this.elements.forEach(callbackfn, thisArg); } public every(callbackfn: (value: ElementType, index: number, array: ReadonlyArray<ElementType>) => unknown, thisArg?: any): boolean { return this.elements.every(callbackfn, thisArg); } public some(callbackfn: (value: ElementType, index: number, array: ReadonlyArray<ElementType>) => unknown, thisArg?: any): boolean { return this.elements.some(callbackfn, thisArg); } public find<S extends ElementType>(predicate: (value: ElementType, index: number, array: ReadonlyArray<ElementType>) => value is S, thisArg?: any): S | undefined { return this.elements.find(predicate, thisArg); } public findIndex(predicate: (value: ElementType, index: number, array: ReadonlyArray<ElementType>) => unknown, thisArg?: any): number { return this.elements.findIndex(predicate, thisArg); } public includes(searchElement: ElementType, fromIndex?: number): boolean { return this.elements.includes(searchElement, fromIndex); } public indexOf(searchElement: ElementType, fromIndex?: number): number { return this.elements.indexOf(searchElement, fromIndex); } public lastIndexOf(searchElement: ElementType, fromIndex?: number): number { return this.elements.lastIndexOf(searchElement, fromIndex); } public join(separator?: string): string { return this.elements.join(separator); } public slice(start?: number, end?: number): ReadonlyArray<ElementType> { return this.elements.slice(start, end); } public map<U>(callbackfn: (value: ElementType, index: number, array: ReadonlyArray<ElementType>) => U, thisArg?: any): U[] { return this.elements.map(callbackfn, thisArg); } public filter<S extends ElementType>(predicate: (value: ElementType, index: number, array: ReadonlyArray<ElementType>) => value is S, thisArg?: any): S[] { return this.elements.filter(predicate, thisArg); } public reduce<U>(callbackfn: (previousValue: U, currentValue: ElementType, currentIndex: number, array: ReadonlyArray<ElementType>) => U, initialValue: U): U { return this.elements.reduce(callbackfn, initialValue); } public reduceRight<U>(callbackfn: (previousValue: U, currentValue: ElementType, currentIndex: number, array: ReadonlyArray<ElementType>) => U, initialValue: U): U { return this.elements.reduceRight(callbackfn, initialValue); } // 自定义静态工厂方法 public static fromArray<ElementType>(array: ReadonlyArray<ElementType>): ReadonlyRowVector<ElementType> { return new ReadonlyRowVector(...array); } constructor(...elements: ReadonlyArray<ElementType>) { this.elements = [...elements]; // 实现数组式迭代 Object.defineProperty(this, Symbol.iterator, { value: () => this.elements[Symbol.iterator]() }); // 实现索引访问 elements.forEach((elem, idx) => { Object.defineProperty(this, idx, { get: () => this.elements[idx], enumerable: true }); }); } // 自定义从1开始计数的元素获取方法 public getElementAt__numerationFrom1(targetElementNumber__numerationFrom1: number): ElementType { if (!isNaturalNumber(targetElementNumber__numerationFrom1)) { Logger.throwErrorAndLog({ errorInstance: new InvalidParameterValueError({ parameterNumber: 1, parameterName: "targetElementNumber__numerationFrom1", messageSpecificPart: [ "目标元素序号必须是自然数", ...isNumber(targetElementNumber__numerationFrom1) ? `但当前值为 ${targetElementNumber__numerationFrom1},并非自然数。` : `但当前类型为 "${typeof targetElementNumber__numerationFrom1}"。` ].join(" ") }), title: InvalidParameterValueError.localization.defaultTitle, occurrenceLocation: "RowVector.getElementAt__numerationFrom1(targetElementNumber__numerationFrom1)" }); } if (targetElementNumber__numerationFrom1 > this.length) { Logger.throwErrorAndLog({ errorInstance: new InvalidParameterValueError({ parameterNumber: 1, parameterName: "targetElementNumber__numerationFrom1", messageSpecificPart: `请求的元素序号为 ${targetElementNumber__numerationFrom1}(从1开始计数),但目标行向量仅包含 ${this.length} 个元素。` }), title: InvalidParameterValueError.localization.defaultTitle, occurrenceLocation: "RowVector.getElementAt__numerationFrom1(targetElementNumber__numerationFrom1)" }); } return this.elements[targetElementNumber__numerationFrom1 - 1]; } }
方案优势
- 完全只读:内部数据通过
private readonly保证不可变,不会暴露Array的可变方法(如push/pop)。 - 类型兼容:实现
ReadonlyArray接口,可直接作为只读数组使用,支持所有只读数组的操作。 - instanceof校验:作为类,支持
instanceof ReadonlyRowVector和instanceof ReadonlyColumnVector这类线性代数场景下的类型校验。 - 自定义扩展:可以自由添加
fromArray、getElementAt__numerationFrom1等业务相关方法。
内容的提问来源于stack exchange,提问作者Takeshi Tokugawa YD
相关产品推荐
相关产品推荐

