diff --git a/packages/@cdklabs/typewriter/src/interface.ts b/packages/@cdklabs/typewriter/src/interface.ts index 07a472345..093546d37 100644 --- a/packages/@cdklabs/typewriter/src/interface.ts +++ b/packages/@cdklabs/typewriter/src/interface.ts @@ -1,5 +1,5 @@ import { MemberType } from './member-type'; -import { PropertySpec } from './property'; +import { Property, PropertySpec } from './property'; import { IScope } from './scope'; import { Type } from './type'; import { DeclarationKind, TypeSpec } from './type-declaration'; @@ -44,4 +44,13 @@ export class InterfaceType extends MemberType { public update, 'properties' | 'methods' | 'name'>>(updates: A) { Object.assign(this.spec, updates); } + + /** + * Adds a property to the interface + * + * Interface properties must be public. + */ + public addProperty(spec: Omit): Property { + return super.addProperty(spec); + } } diff --git a/packages/@cdklabs/typewriter/src/member-type.ts b/packages/@cdklabs/typewriter/src/member-type.ts index 787a1d5c8..d60ac16a7 100644 --- a/packages/@cdklabs/typewriter/src/member-type.ts +++ b/packages/@cdklabs/typewriter/src/member-type.ts @@ -29,6 +29,10 @@ export abstract class MemberType extends TypeDeclaration { * Adds a property to the interface */ public addProperty(spec: PropertySpec): Property { + if (spec.protected && spec.visibility) { + throw new Error('Cannot specify both "protected" and "visibility"'); + } + const prop = new Property(this, { ...spec, }); diff --git a/packages/@cdklabs/typewriter/src/property.ts b/packages/@cdklabs/typewriter/src/property.ts index 58446c563..a1464d0a3 100644 --- a/packages/@cdklabs/typewriter/src/property.ts +++ b/packages/@cdklabs/typewriter/src/property.ts @@ -21,9 +21,20 @@ export interface PropertySpec { /** * Indicates if this property is protected (otherwise it is public) * + * @deprecated Use visibility instead. * @default false */ protected?: boolean; + + /** + * Indicates the visibility of this property + * + * Cannot be used together with `protected`. + * + * @default false + */ + visibility?: MemberVisibility; + /** * Indicates if this property is abstract * @@ -106,6 +117,9 @@ export class Property extends TypeMember implements IProperty { * The visibility of the member. */ public get visibility(): MemberVisibility { + if (this.spec?.visibility !== undefined) { + return this.spec.visibility; + } if (this.spec?.protected) { return MemberVisibility.Protected; } diff --git a/packages/@cdklabs/typewriter/src/struct.ts b/packages/@cdklabs/typewriter/src/struct.ts index 19b9a02cf..a48f4d3b3 100644 --- a/packages/@cdklabs/typewriter/src/struct.ts +++ b/packages/@cdklabs/typewriter/src/struct.ts @@ -38,8 +38,10 @@ export class StructType extends MemberType { /** * Adds a property to the interface + * + * Struct members are always public and immutable. */ - public addProperty(spec: Omit): Property { + public addProperty(spec: Omit): Property { return super.addProperty({ ...spec, immutable: true, diff --git a/packages/@cdklabs/typewriter/test/class.test.ts b/packages/@cdklabs/typewriter/test/class.test.ts index 2f3f8f89e..3f589a6e3 100644 --- a/packages/@cdklabs/typewriter/test/class.test.ts +++ b/packages/@cdklabs/typewriter/test/class.test.ts @@ -1,4 +1,4 @@ -import { Block, ClassType, MemberVisibility, Module, TypeScriptRenderer } from '../src'; +import { Block, ClassType, expr, MemberVisibility, Module, Type, TypeScriptRenderer } from '../src'; const renderer = new TypeScriptRenderer(); let scope: Module; @@ -26,6 +26,25 @@ test('class with a private constructor', () => { `); }); +test('class with a private property', () => { + const c = new ClassType(scope, { + name: 'MyClass', + }); + c.addProperty({ + name: 'myProperty', + type: Type.STRING, + visibility: MemberVisibility.Private, + initializer: expr.lit('Hello World'), + }); + + expect(renderer.render(scope)).toMatchInlineSnapshot(` + "/* eslint-disable prettier/prettier, @stylistic/max-len */ + class MyClass { + private myProperty: string = "Hello World"; + }" + `); +}); + test('can update some class spec fields after initial creation', () => { const c = new ClassType(scope, { name: 'MyClass',