Skip to content
This repository has been archived by the owner on Oct 15, 2018. It is now read-only.

Commit

Permalink
#173 - Support abstract properties
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Aug 6, 2016
1 parent 4e7b8d7 commit 9ced35e
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 45 deletions.
7 changes: 6 additions & 1 deletion src/binders/base/class/ClassPropertyBinder.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import CodeBlockWriter from "code-block-writer";
import {ClassPropertyDefinition} from "./../../../definitions";
import {AbstractableBinder} from "./../base";
import {IBaseBinder} from "./../IBaseBinder";
import {BaseClassPropertyBinder} from "./base";

export abstract class ClassPropertyBinder implements IBaseBinder {
constructor(private readonly baseClassPropertyBinder: BaseClassPropertyBinder) {
constructor(
private readonly baseClassPropertyBinder: BaseClassPropertyBinder,
private readonly abstractableBinder: AbstractableBinder
) {
}

abstract getIsAccessor(): boolean;
Expand All @@ -14,6 +18,7 @@ export abstract class ClassPropertyBinder implements IBaseBinder {

bind(def: ClassPropertyDefinition) {
this.baseClassPropertyBinder.bind(def);
this.abstractableBinder.bind(def);
def.isAccessor = this.getIsAccessor();
def.isReadonly = this.getIsReadonly();
def.onWriteGetBody = this.getOnWriteGetBody();
Expand Down
6 changes: 5 additions & 1 deletion src/binders/structure/class/StructureClassPropertyBinder.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import {StructureFactory} from "./../../../factories";
import {ClassPropertyStructure} from "./../../../structures";
import {ClassPropertyBinder} from "./../../base";
import {StructureAbstractableBinder} from "./../base";
import {StructureBaseClassPropertyBinder} from "./base";

export class StructureClassPropertyBinder extends ClassPropertyBinder {
constructor(factory: StructureFactory, private readonly structure: ClassPropertyStructure) {
super(new StructureBaseClassPropertyBinder(factory, structure));
super(
new StructureBaseClassPropertyBinder(factory, structure),
new StructureAbstractableBinder(structure)
);
}

getIsAccessor() {
Expand Down
6 changes: 5 additions & 1 deletion src/binders/ts/class/TsClassPropertyBinder.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import {TsFactory} from "./../../../factories";
import {TsNode, TsSymbol} from "./../../../compiler";
import {ClassPropertyBinder} from "./../../base";
import {TsAbstractableBinder} from "./../base";
import {TsBaseClassPropertyBinder} from "./base";

export class TsClassPropertyBinder extends ClassPropertyBinder {
private readonly symbol: TsSymbol;

constructor(factory: TsFactory, node: TsNode) {
super(new TsBaseClassPropertyBinder(factory, node));
super(
new TsBaseClassPropertyBinder(factory, node),
new TsAbstractableBinder(node)
);
this.symbol = node.getSymbol()!;
}

Expand Down
10 changes: 8 additions & 2 deletions src/definitions/class/ClassPropertyDefinition.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import CodeBlockWriter from "code-block-writer";
import {DefinitionType} from "./../base";
import {applyMixins} from "./../../utils";
import {DefinitionType, AbstractableDefinition} from "./../base";
import {BaseClassPropertyDefinition} from "./base";

export class ClassPropertyDefinition extends BaseClassPropertyDefinition {
export class ClassPropertyDefinition extends BaseClassPropertyDefinition implements AbstractableDefinition {
isAccessor: boolean;
isReadonly: boolean;
isConstructorParameter: boolean;
Expand All @@ -13,4 +14,9 @@ export class ClassPropertyDefinition extends BaseClassPropertyDefinition {
constructor() {
super(DefinitionType.ClassProperty);
}

// AbstractableDefinition
isAbstract: boolean;
}

applyMixins(ClassPropertyDefinition, BaseClassPropertyDefinition, [AbstractableDefinition]);
3 changes: 2 additions & 1 deletion src/structures/class/ClassPropertyStructure.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import CodeBlockWriter from "code-block-writer";
import {AbstractableStructure} from "./../base";
import {BaseClassPropertyStructure} from "./base";

export interface ClassPropertyStructure extends BaseClassPropertyStructure {
export interface ClassPropertyStructure extends BaseClassPropertyStructure, AbstractableStructure {
isAccessor?: boolean;
isReadonly?: boolean;
onWriteGetBody?: (writer: CodeBlockWriter) => void;
Expand Down
2 changes: 2 additions & 0 deletions src/tests/definitions/class/classManipulationTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ describe("ClassDefinition", () => {
describe("#addProperty()", () => {
const c = new ClassDefinition();
const returnedDef = c.addProperty({
isAbstract: true,
decorators: [{ name: "decorator" }],
defaultExpression: "4",
isAccessor: true,
Expand All @@ -197,6 +198,7 @@ describe("ClassDefinition", () => {
});

testHelpers.runClassPropertyDefinitionTests(c.properties[0], {
isAbstract: true,
decorators: [{ name: "decorator" }],
defaultExpression: { text: "4" },
isAccessor: true,
Expand Down
14 changes: 10 additions & 4 deletions src/tests/definitions/class/classWriteTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ abstract class MyClass {
private myPrivateString: string;
nonOptionalString = "text";
optionalNumber?: number;
protected abstract myAbstractProperty: string;
protected abstract get myAbstractAccessorProperty(): string;
protected abstract set myAbstractAccessorProperty(value: string);
get myGet(): string {
return "";
Expand All @@ -27,11 +30,11 @@ abstract class MyClass {
set myGetSet(value: string) {
}
get myGetSetWithWriteDefined(): string {
protected get myGetSetWithWriteDefined(): string {
return "";
}
set myGetSetWithWriteDefined(value: string) {
protected set myGetSetWithWriteDefined(value: string) {
}
constructor(myParam: string, public myPublicParam: any, protected myProtectedParam: any, private myPrivateParam: any) {
Expand Down Expand Up @@ -108,14 +111,17 @@ describe("ClassDefinition", () => {
private myPrivateString: string;
nonOptionalString = "text";
optionalNumber?: number;
protected abstract myAbstractProperty: string;
protected abstract get myAbstractAccessorProperty(): string;
protected abstract set myAbstractAccessorProperty(value: string);
myGet: string;
myGetSet: string;
get myGetSetWithWriteDefined(): string {
protected get myGetSetWithWriteDefined(): string {
return "";
}
set myGetSetWithWriteDefined(value: string) {
protected set myGetSetWithWriteDefined(value: string) {
alert(value);
}
Expand Down
6 changes: 6 additions & 0 deletions src/tests/languageTests/class/propertyTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class MyClass {
}
set myGetAndSetAccessor(val: string) {
}
abstract myAbstractProperty: string;
}`;

const def = getInfoFromString(code);
Expand Down Expand Up @@ -65,6 +67,10 @@ class MyClass {
name: "myGetAndSetAccessor",
type: { text: "string" },
isAccessor: true
}, {
name: "myAbstractProperty",
isAbstract: true,
type: { text: "string" }
}]
}]
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import * as assert from "assert";
import {ClassPropertyTestStructure} from "./../../testStructures";
import {ClassPropertyDefinition} from "./../../../../definitions";
import {runAbstractableDefinitionTests} from "./../base";
import {runBaseClassPropertyDefinitionTests} from "./base";
import {ensureNotNull} from "./../../ensureNotNull";

export function runClassPropertyDefinitionTests(definition: ClassPropertyDefinition, structure: ClassPropertyTestStructure) {
describe(`property ${structure.name}`, () => {
ensureNotNull(definition, () => {
runBaseClassPropertyDefinitionTests(definition, structure);
runAbstractableDefinitionTests(definition, structure);

it(`should be ${structure.isAccessor ? "an accessor" : "not an accessor"}`, () => {
assert.equal(definition.isAccessor, structure.isAccessor || false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {AbstractableTestStructure} from "./../base";
import {BaseClassPropertyTestStructure} from "./base";

export interface ClassPropertyTestStructure extends BaseClassPropertyTestStructure {
export interface ClassPropertyTestStructure extends BaseClassPropertyTestStructure, AbstractableTestStructure {
isAccessor?: boolean;
isReadonly?: boolean;
hasOnWriteGetBody?: boolean;
Expand Down
77 changes: 43 additions & 34 deletions src/writers/PropertyWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,21 @@ export class PropertyWriter extends BaseDefinitionWriter<PropertyDefinitions> {
private readonly scopeWriter = new ScopeWriter(this.writer);
private readonly typeWithDefaultExpressionWriter = new TypeWithDefaultExpressionWriter(this.writer);

static willWriteAccessorBody(def: PropertyDefinitions) {
return def.isClassPropertyDefinition() && def.isAccessor && (def.onWriteGetBody != null || def.onWriteSetBody != null);
static willWriteAccessorBody(def: PropertyDefinitions): def is ClassPropertyDefinition {
return PropertyWriter.isAccessor(def) && (def.onWriteGetBody != null || def.onWriteSetBody != null);
}

private static isAbstractAccessor(def: PropertyDefinitions): def is ClassPropertyDefinition {
return PropertyWriter.isAccessor(def) && def.isAbstract;
}

private static isAccessor(def: PropertyDefinitions): def is ClassPropertyDefinition {
return def.isClassPropertyDefinition() && def.isAccessor;
}

protected writeDefault(def: PropertyDefinitions, flags: WriteFlags) {
if (PropertyWriter.willWriteAccessorBody(def)) {
this.writeAccessor(def as ClassPropertyDefinition);
if (PropertyWriter.willWriteAccessorBody(def) || PropertyWriter.isAbstractAccessor(def)) {
this.writeAccessor(def);
}
else {
this.writeNormalProperty(def, flags);
Expand All @@ -27,41 +35,54 @@ export class PropertyWriter extends BaseDefinitionWriter<PropertyDefinitions> {
this.writeGetAccessor(def);

if (!def.isReadonly) {
this.writer.newLine();
this.writer.conditionalNewLine(!def.isAbstract);
this.writeSetAccessor(def);
}
}

private writeGetAccessor(def: ClassPropertyDefinition) {
this.writeCommonHeader(def);
this.writer.write("get ");
this.writeHeader(def);
this.writer.write(def.name);
this.writer.write("()");
this.typeWriter.writeWithColon(def.type);

this.writer.block(() => {
if (typeof def.onWriteGetBody === "function") {
def.onWriteGetBody(this.writer);
}
});
if (def.isAbstract) {
this.writer.write(";").newLine();
}
else {
this.writer.block(() => {
if (typeof def.onWriteGetBody === "function") {
def.onWriteGetBody(this.writer);
}
});
}
}

private writeSetAccessor(def: ClassPropertyDefinition) {
this.writeCommonHeader(def);
this.writer.write("set ");
this.writeHeader(def);
this.writer.write(def.name);
this.writer.write("(value"); // default to value for now
this.typeWriter.writeWithColon(def.type);
this.writer.write(")");

this.writer.block(() => {
if (typeof def.onWriteSetBody === "function") {
def.onWriteSetBody(this.writer);
}
});
if (def.isAbstract) {
this.writer.write(";").newLine();
}
else {
this.writer.block(() => {
if (typeof def.onWriteSetBody === "function") {
def.onWriteSetBody(this.writer);
}
});
}
}

private writeNormalProperty(def: PropertyDefinitions, flags: WriteFlags) {
this.writeHeader(def);
this.writeOptionalFlag(def);
this.writeCommonHeader(def);
this.writer.write(def.name);
this.writer.conditionalWrite(def.isOptional, "?");

if (def.isInterfacePropertyDefinition()) {
this.typeWriter.writeWithColon(def.type);
Expand All @@ -73,22 +94,10 @@ export class PropertyWriter extends BaseDefinitionWriter<PropertyDefinitions> {
this.writer.write(";").newLine();
}

private writeHeader(def: PropertyDefinitions) {
private writeCommonHeader(def: PropertyDefinitions) {
this.scopeWriter.write((def as ClassPropertyDefinition).scope);
this.writer.spaceIfLastNotSpace();
this.writeStatic(def);
this.writer.write(def.name);
}

private writeStatic(def: PropertyDefinitions) {
if (def.isClassStaticPropertyDefinition()) {
this.writer.write("static ");
}
}

private writeOptionalFlag(property: PropertyDefinitions) {
if (property.isOptional) {
this.writer.write("?");
}
this.writer.conditionalWrite((def as ClassPropertyDefinition).isAbstract, "abstract ");
this.writer.conditionalWrite(def.isClassStaticPropertyDefinition(), "static ");
}
}

0 comments on commit 9ced35e

Please sign in to comment.