packages/jsii-reflect/lib/class.ts (120 lines of code) (raw):
import * as jsii from '@jsii/spec';
import { memoizedWhenLocked } from './_memoized';
import { Assembly } from './assembly';
import { Initializer } from './initializer';
import { InterfaceType } from './interface';
import { Method } from './method';
import { Property } from './property';
import { ReferenceType } from './reference-type';
import { TypeSystem } from './type-system';
export class ClassType extends ReferenceType {
public constructor(
public readonly system: TypeSystem,
public readonly assembly: Assembly,
public readonly spec: jsii.ClassType,
) {
super(system, assembly, spec);
}
/**
* Base class (optional).
*/
@memoizedWhenLocked
public get base(): ClassType | undefined {
if (!this.spec.base) {
return undefined;
}
const type = this.system.findFqn(this.spec.base);
if (!(type instanceof ClassType)) {
throw new Error(
`FQN for base class points to a non-class type: ${this.spec.base}`,
);
}
return type;
}
/**
* Initializer (constructor) method.
*/
public get initializer(): Initializer | undefined {
if (!this.spec.initializer) {
return undefined;
}
return new Initializer(
this.system,
this.assembly,
this,
this.spec.initializer,
);
}
/**
* Indicates if this class is an abstract class.
*/
public get abstract(): boolean {
return !!this.spec.abstract;
}
/**
* Returns list of all base classes (first is the direct base and last is the top-most).
*
* @deprecated use ClassType.ancestors instead
*/
public getAncestors() {
return this.ancestors;
}
/**
* Returns list of all base classes (first is the direct base and last is the top-most).
*/
@memoizedWhenLocked
public get ancestors() {
const out = new Array<ClassType>();
if (this.base) {
out.push(this.base);
out.push(...this.base.ancestors);
}
return out;
}
/**
* Lists all properties in this class.
* @param inherited include all properties inherited from base classes (default: false)
*/
public getProperties(inherited = false): { [name: string]: Property } {
return Object.fromEntries(this._getProperties(inherited, this));
}
/**
* List all methods in this class.
* @param inherited include all methods inherited from base classes (default: false)
*/
public getMethods(inherited = false): { [name: string]: Method } {
return Object.fromEntries(this._getMethods(inherited, this));
}
/**
* Lists all interfaces this class implements.
* @param inherited include all interfaces implemented by all base classes (default: false)
*/
public getInterfaces(inherited = false) {
const out = new Array<InterfaceType>();
if (inherited && this.base) {
out.push(...this.base.getInterfaces(inherited));
}
if (this.spec.interfaces) {
out.push(
...flatten(
this.spec.interfaces
.map((fqn) => this.system.findInterface(fqn))
.map((iface) => [
iface,
...(inherited ? iface.getInterfaces(true) : []),
]),
),
);
}
return out;
}
public isClassType(): this is ClassType {
return true;
}
private _getProperties(
inherited: boolean,
parentType: ReferenceType,
): Map<string, Property> {
const result =
inherited && this.base
? this.base._getProperties(inherited, parentType)
: new Map<string, Property>();
for (const p of this.spec.properties ?? []) {
result.set(
p.name,
new Property(this.system, this.assembly, parentType, this, p),
);
}
return result;
}
private _getMethods(
inherited: boolean,
parentType: ReferenceType,
): Map<string, Method> {
const result =
inherited && this.base
? this.base._getMethods(inherited, parentType)
: new Map<string, Method>();
for (const m of this.spec.methods ?? []) {
result.set(
m.name,
new Method(this.system, this.assembly, parentType, this, m),
);
}
return result;
}
}
function flatten<A>(xs: A[][]): A[] {
return Array.prototype.concat([], ...xs);
}