Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 32 additions & 31 deletions lib/codegen/fromcto/typescript/typescriptvisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,29 +144,21 @@ class TypescriptVisitor {
const typeName = ModelUtil.getShortName(property.getFullyQualifiedTypeName());
addImport(typeNamespace,
property.isTypeEnum?.() || property.isTypeScalar?.() ? typeName : `I${typeName}`);
}
});

const subclasses = declaration.getDirectSubclasses();
if (subclasses && subclasses.length > 0) {
parameters.fileWriter.writeLine(0, '\n// Warning: Beware of circular dependencies when modifying these imports');

// Group subclasses by namespace
const namespaceBuckets = {};
subclasses.map(subclass => {
const bucket = namespaceBuckets[subclass.getNamespace()];
if (bucket){
bucket.push(subclass);
} else {
namespaceBuckets[subclass.getNamespace()] = [subclass];
// When flattenSubclassesToUnion is set, also import the union type
if (parameters.flattenSubclassesToUnion && !property.isTypeEnum?.() && !property.isTypeScalar?.()) {
const propDecl = modelFile.getModelManager().getType(property.getFullyQualifiedTypeName());
const subclasses = propDecl?.getDirectSubclasses?.();
if (subclasses) {
const sameNsSubs = subclasses.filter(sc =>
!sc.isEnum() && sc.getNamespace() === propDecl.getNamespace());
if (sameNsSubs.length > 1) {
addImport(typeNamespace, `${typeName}Union`);
}
}
}
});
Object.entries(namespaceBuckets)
.filter(([namespace]) => namespace !== modelFile.getNamespace()) // Skip own namespace
.map(([namespace, bucket]) => {
parameters.fileWriter.writeLine(0, `import type {\n\t${bucket.map(subclass => subclass.isEnum() ? subclass.getName() : `I${subclass.getName()}`).join(',\n\t') }\n} from './${namespace}';`);
});
}
}
});
}
});

Expand Down Expand Up @@ -245,11 +237,15 @@ class TypescriptVisitor {

parameters.fileWriter.writeLine(0, '}\n');

// If there exists direct subclasses for this declaration then generate a union for it
// Generate a union alias only when there are multiple same-namespace subclasses
const subclasses = classDeclaration.getDirectSubclasses();
if (subclasses && subclasses.length > 0) {
parameters.fileWriter.writeLine(0, 'export type ' + classDeclaration.getName() +
'Union = ' + subclasses.filter(declaration => !declaration.isEnum()).map(subclass => `I${subclass.getName()}`).join(' | \n') + ';\n');
if (subclasses) {
const sameNsSubclasses = subclasses
.filter(sc => !sc.isEnum() && sc.getNamespace() === classDeclaration.getNamespace());
if (sameNsSubclasses.length > 1) {
parameters.fileWriter.writeLine(0, 'export type ' + classDeclaration.getName() +
'Union = ' + sameNsSubclasses.map(subclass => `I${subclass.getName()}`).join(' | \n') + ';\n');
}
}
return null;
}
Expand Down Expand Up @@ -301,12 +297,17 @@ class TypescriptVisitor {

let tsType = this.toTsType(field.getType(), !isEnumRef && !hasUnion && !isMapRef, hasUnion);

// If there exists direct subclasses for this field's declaration then use the union type instead
if (!!parameters.flattenSubclassesToUnion & !field.isPrimitive()) {
const subclasses = field.getParent().getModelFile().getModelManager().getType(field.getFullyQualifiedTypeName()).getDirectSubclasses();
if (subclasses && subclasses.length > 0) {
const useUnion = !(isEnumRef || isMapRef);
tsType = this.toTsType(field.getType(), !useUnion, useUnion);
// Use the union type only when there are multiple same-namespace subclasses
if (!!parameters.flattenSubclassesToUnion && !field.isPrimitive()) {
const fieldDecl = field.getParent().getModelFile().getModelManager().getType(field.getFullyQualifiedTypeName());
const subclasses = fieldDecl.getDirectSubclasses();
if (subclasses) {
const sameNsSubclasses = subclasses
.filter(sc => !sc.isEnum() && sc.getNamespace() === fieldDecl.getNamespace());
if (sameNsSubclasses.length > 1) {
const useUnion = !(isEnumRef || isMapRef);
tsType = this.toTsType(field.getType(), !useUnion, useUnion);
}
Comment on lines +307 to +310
}
}

Expand Down
75 changes: 0 additions & 75 deletions test/codegen/__snapshots__/codegen.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6753,19 +6753,12 @@ exports[`codegen #formats check we can convert all formats from namespace versio
// Generated code for namespace: concerto.decorator@1.0.0

// imports

// Warning: Beware of circular dependencies when modifying these imports
import type {
Ipii
} from './org.acme.hr@1.0.0';
import {IConcept} from './concerto@1.0.0';

// interfaces
export interface IDecorator extends IConcept {
}

export type DecoratorUnion = Ipii;

export interface IDotNetNamespace extends IDecorator {
namespace: string;
}
Expand All @@ -6782,77 +6775,27 @@ exports[`codegen #formats check we can convert all formats from namespace versio

// imports

// Warning: Beware of circular dependencies when modifying these imports
import type {
ICategory,
State,
TShirtSizeType,
IAddress,
Level
} from './org.acme.hr.base@1.0.0';
import type {
ICategory,
IInfo,
ICompany,
Department,
LaptopMake
} from './org.acme.hr@1.0.0';

// Warning: Beware of circular dependencies when modifying these imports
import type {
IEquipment
} from './org.acme.hr@1.0.0';

// Warning: Beware of circular dependencies when modifying these imports
import type {
IPerson
} from './org.acme.hr@1.0.0';

// Warning: Beware of circular dependencies when modifying these imports
import type {
IChangeOfAddress
} from './org.acme.hr@1.0.0';

// Warning: Beware of circular dependencies when modifying these imports
import type {
ICompanyEvent
} from './org.acme.hr@1.0.0';

// interfaces
export interface IConcept {
$class: string;
}

export type ConceptUnion = ICategory |
IAddress |
ICategory |
IInfo |
ICompany;

export interface IAsset extends IConcept {
$identifier: string;
}

export type AssetUnion = IEquipment;

export interface IParticipant extends IConcept {
$identifier: string;
}

export type ParticipantUnion = IPerson;

export interface ITransaction extends IConcept {
$timestamp: Date;
}

export type TransactionUnion = IChangeOfAddress;

export interface IEvent extends IConcept {
$timestamp: Date;
}

export type EventUnion = ICompanyEvent;

",
}
`;
Expand All @@ -6864,16 +6807,12 @@ exports[`codegen #formats check we can convert all formats from namespace versio
// Generated code for namespace: org.acme.hr.base@1.0.0

// imports

// Warning: Beware of circular dependencies when modifying these imports
import {IConcept} from './concerto@1.0.0';

// interfaces
export interface ICategory extends IConcept {
}

export type CategoryUnion = IGeneralCategory;

export interface IGeneralCategory extends ICategory {
}

Expand Down Expand Up @@ -6920,14 +6859,6 @@ exports[`codegen #formats check we can convert all formats from namespace versio
// Generated code for namespace: org.acme.hr@1.0.0

// imports

// Warning: Beware of circular dependencies when modifying these imports

// Warning: Beware of circular dependencies when modifying these imports

// Warning: Beware of circular dependencies when modifying these imports

// Warning: Beware of circular dependencies when modifying these imports
import {Time,SSN,IAddress,IEmployeeTShirtSizes} from './org.acme.hr.base@1.0.0';
import {IDecorator} from './concerto.decorator@1.0.0';
import {IConcept,IAsset,IParticipant,IEvent,ITransaction} from './concerto@1.0.0';
Expand Down Expand Up @@ -6980,8 +6911,6 @@ export interface IEquipment extends IAsset {
serialNumber: string;
}

export type EquipmentUnion = ILaptop;

export enum LaptopMake {
Apple = 'Apple',
Microsoft = 'Microsoft',
Expand Down Expand Up @@ -7017,8 +6946,6 @@ export interface IEmployee extends IPerson {
manager?: IManager;
}

export type EmployeeUnion = IManager;

export interface IContractor extends IPerson {
company: ICompany;
manager?: IManager;
Expand All @@ -7031,8 +6958,6 @@ export interface IManager extends IEmployee {
export interface ICompanyEvent extends IEvent {
}

export type CompanyEventUnion = IOnboarded;

export interface IOnboarded extends ICompanyEvent {
employee: IEmployee;
}
Expand Down
Loading
Loading