Skip to content
Fix Code Error

TypeScript metadata reflection references other classes before they are defined

April 30, 2021 by Code Error
Posted By: Anonymous

I have some TypeORM entities in my codebase which have relations to each other, making a circular dependency. Since decorator metadata is used on each entity class, TypeScript inserts code after each class defining metadata on it. Say that the classes are Business and Qualification. On the relating fields TypeScript will emit code that looks like this:

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
const decorator = (target, thing) => {
};
class Business {
}
class Qualification {
}
__decorate([
    decorator,
    __metadata("design:type", Business)
], Qualification.prototype, "business", void 0);

This would all be fine, except the __decorate part always comes after each class, meaning that one of the classes is going to have to be used before it’s defined, which causes an error. Here’s a shortened version of the actual code with the actual error:

let Qualification = (_dec = Object(external_typeorm_["Entity"])(), _dec2 = Object(external_typeorm_["PrimaryGeneratedColumn"])(), _dec3 = Reflect.metadata("design:type", Number), _dec4 = Object(external_typeorm_["Column"])({
  nullable: true
}), _dec5 = Object(external_class_validator_["IsOptional"])(), _dec6 = Object(external_class_validator_["IsUrl"])(), _dec7 = Reflect.metadata("design:type", String), _dec8 = Object(external_typeorm_["ManyToOne"])(type => Business["c" /* default */], business => business.qualifications, {
  onDelete: 'CASCADE'
}), _dec9 = Reflect.metadata("design:type", typeof Business["c" /* default */] === "undefined" ?

                                                // ^ TypeError: cannot read property "c" of undefined



 Object : Business["c" /* default */]), _dec10 = Object(external_typeorm_["Column"])({
  type: 'enum',
  enum: VALIDITY_STATES,
  default: 'invalid'
}), _dec11 = Object(external_class_validator_["IsIn"])(VALIDITY_STATES), _dec12 = Reflect.metadata("design:type", Object), _dec13 = Object(external_typeorm_["Column"])('simple-json'), _dec14 = Object(external_class_validator_["ValidateNested"])(), _dec15 = Object(external_class_validator_["IsArray"])(), _dec16 = Object(external_class_validator_["IsIn"])(category["a" /* CATEGORIES */].filter(c => c.type === 'service').map(c => c.slug), {
  each: true
}), _dec17 = Reflect.metadata("design:type", Array), _dec(_class = (_class2 = (_temp = class Qualification {
  constructor() {
    _initializerDefineProperty(this, "id", _descriptor, this);

    _initializerDefineProperty(this, "imageUrl", _descriptor2, this);

    _initializerDefineProperty(this, "business", _descriptor3, this);

    _initializerDefineProperty(this, "validity", _descriptor4, this);

    _initializerDefineProperty(this, "categories", _descriptor5, this);
  }

}, _temp), (_descriptor = _applyDecoratedDescriptor(_class2.prototype, "id", [_dec2, _dec3], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor2 = _applyDecoratedDescriptor(_class2.prototype, "imageUrl", [_dec4, _dec5, _dec6, _dec7], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor3 = _applyDecoratedDescriptor(_class2.prototype, "business", [_dec8, _dec9], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor4 = _applyDecoratedDescriptor(_class2.prototype, "validity", [_dec10, _dec11, _dec12], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor5 = _applyDecoratedDescriptor(_class2.prototype, "categories", [_dec13, _dec14, _dec15, _dec16, _dec17], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
})), _class2)) || _class);

Later in the code, Business is defined, but it’s too late:

let Business = (_dec6 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Entity"])(), _dec7 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])(), _dec8 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsString"])(), _dec9 = Reflect.metadata("design:type", String), _dec10 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])({
  type: 'enum',
  enum: BUSINESS_TYPES
}), _dec11 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsIn"])(BUSINESS_TYPES), _dec12 = Reflect.metadata("design:type", Object), _dec13 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])(), _dec14 = Reflect.metadata("design:type", Boolean), _dec15 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["CreateDateColumn"])(), _dec16 = Reflect.metadata("design:type", typeof Date === "undefined" ? Object : Date), _dec17 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])({
  nullable: true
}), _dec18 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsOptional"])(), _dec19 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsUrl"])(), _dec20 = Reflect.metadata("design:type", String), _dec21 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])({
  nullable: true
}), _dec22 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsOptional"])(), _dec23 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsString"])(), _dec24 = Reflect.metadata("design:type", String), _dec25 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])({
  nullable: true
}), _dec26 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsOptional"])(), _dec27 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsString"])(), _dec28 = Reflect.metadata("design:type", String), _dec29 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])(), _dec30 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsString"])(), _dec31 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["MaxLength"])(200), _dec32 = Reflect.metadata("design:type", String), _dec33 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["OneToMany"])(type => _db_all_entities__WEBPACK_IMPORTED_MODULE_3__[/* Qualification */ "i"], qualification => qualification.business, {
  cascade: true
}), _dec34 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["ValidateNested"])(), _dec35 = Reflect.metadata("design:type", Array), _dec36 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])('simple-json'), _dec37 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsArray"])(), _dec38 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["ValidateNested"])(), _dec39 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsIn"])(_misc_types_category__WEBPACK_IMPORTED_MODULE_2__[/* CATEGORIES */ "a"].filter(c => c.type === 'business').map(c => c.slug), {
  each: true
}), _dec40 = Reflect.metadata("design:type", Array), _dec41 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])('simple-json'), _dec42 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsArray"])(), _dec43 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["ValidateNested"])(), _dec44 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["ArrayMinSize"])(1), _dec45 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["ArrayMaxSize"])(5), _dec46 = Reflect.metadata("design:type", Array), _dec47 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])({
  type: 'enum',
  enum: PRICING_PLANS
}), _dec48 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsIn"])(PRICING_PLANS), _dec49 = Reflect.metadata("design:type", String), _dec50 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["OneToMany"])(type => _db_all_entities__WEBPACK_IMPORTED_MODULE_3__[/* BaseOffer */ "b"], offer => offer.offerer), _dec51 = Reflect.metadata("design:type", Array), _dec6(_class3 = (_class4 = (_temp2 = class Business extends _db_all_entities__WEBPACK_IMPORTED_MODULE_3__[/* Account */ "a"] {
  constructor(...args) {
    super(...args);

    _initializerDefineProperty(this, "name", _descriptor3, this);

    _initializerDefineProperty(this, "type", _descriptor4, this);

    _initializerDefineProperty(this, "isApproved", _descriptor5, this);

    _initializerDefineProperty(this, "since", _descriptor6, this);

    _initializerDefineProperty(this, "logoUrl", _descriptor7, this);

    _initializerDefineProperty(this, "fein", _descriptor8, this);

    _initializerDefineProperty(this, "phoneNumber", _descriptor9, this);

    _initializerDefineProperty(this, "bio", _descriptor10, this);

    _initializerDefineProperty(this, "qualifications", _descriptor11, this);

    _initializerDefineProperty(this, "businessCategories", _descriptor12, this);

    _initializerDefineProperty(this, "geolocations", _descriptor13, this);

    _initializerDefineProperty(this, "pricingPlan", _descriptor14, this);

    _initializerDefineProperty(this, "offers", _descriptor15, this);
  }

}, _temp2), (_descriptor3 = _applyDecoratedDescriptor(_class4.prototype, "name", [_dec7, _dec8, _dec9], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor4 = _applyDecoratedDescriptor(_class4.prototype, "type", [_dec10, _dec11, _dec12], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor5 = _applyDecoratedDescriptor(_class4.prototype, "isApproved", [_dec13, _dec14], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor6 = _applyDecoratedDescriptor(_class4.prototype, "since", [_dec15, _dec16], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor7 = _applyDecoratedDescriptor(_class4.prototype, "logoUrl", [_dec17, _dec18, _dec19, _dec20], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor8 = _applyDecoratedDescriptor(_class4.prototype, "fein", [_dec21, _dec22, _dec23, _dec24], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor9 = _applyDecoratedDescriptor(_class4.prototype, "phoneNumber", [_dec25, _dec26, _dec27, _dec28], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor10 = _applyDecoratedDescriptor(_class4.prototype, "bio", [_dec29, _dec30, _dec31, _dec32], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor11 = _applyDecoratedDescriptor(_class4.prototype, "qualifications", [_dec33, _dec34, _dec35], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor12 = _applyDecoratedDescriptor(_class4.prototype, "businessCategories", [_dec36, _dec37, _dec38, _dec39, _dec40], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor13 = _applyDecoratedDescriptor(_class4.prototype, "geolocations", [_dec41, _dec42, _dec43, _dec44, _dec45, _dec46], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor14 = _applyDecoratedDescriptor(_class4.prototype, "pricingPlan", [_dec47, _dec48, _dec49], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor15 = _applyDecoratedDescriptor(_class4.prototype, "offers", [_dec50, _dec51], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
})), _class4)) || _class3);

Strangely, the code works when compiled for development mode because Business is not referred to directly but rather through a module constant. Here’s how Qualification is defined in development mode:

let Qualification = (_dec = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Entity"])(), _dec2 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["PrimaryGeneratedColumn"])(), _dec3 = Reflect.metadata("design:type", Number), _dec4 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])({
  nullable: true
}), _dec5 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsOptional"])(), _dec6 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsUrl"])(), _dec7 = Reflect.metadata("design:type", String), _dec8 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["ManyToOne"])(type => _db_all_entities__WEBPACK_IMPORTED_MODULE_2__["Business"], business => business.qualifications, {
  onDelete: 'CASCADE'
}), _dec9 = Reflect.metadata("design:type", typeof _db_all_entities__WEBPACK_IMPORTED_MODULE_2__["Business"] === "undefined" ? Object : _db_all_entities__WEBPACK_IMPORTED_MODULE_2__["Business"]), _dec10 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])({
  type: 'enum',
  enum: VALIDITY_STATES,
  default: 'invalid'
}), _dec11 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsIn"])(VALIDITY_STATES), _dec12 = Reflect.metadata("design:type", Object), _dec13 = Object(typeorm__WEBPACK_IMPORTED_MODULE_1__["Column"])('simple-json'), _dec14 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["ValidateNested"])(), _dec15 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsArray"])(), _dec16 = Object(class_validator__WEBPACK_IMPORTED_MODULE_0__["IsIn"])(_misc_types_category__WEBPACK_IMPORTED_MODULE_5__["CATEGORIES"].filter(c => c.type === 'service').map(c => c.slug), {
  each: true
}), _dec17 = Reflect.metadata("design:type", Array), _dec(_class = (_class2 = (_temp = class Qualification {
  constructor() {
    _initializerDefineProperty(this, "id", _descriptor, this);

    _initializerDefineProperty(this, "imageUrl", _descriptor2, this);

    _initializerDefineProperty(this, "business", _descriptor3, this);

    _initializerDefineProperty(this, "validity", _descriptor4, this);

    _initializerDefineProperty(this, "categories", _descriptor5, this);
  }

}, _temp), (_descriptor = _applyDecoratedDescriptor(_class2.prototype, "id", [_dec2, _dec3], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor2 = _applyDecoratedDescriptor(_class2.prototype, "imageUrl", [_dec4, _dec5, _dec6, _dec7], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor3 = _applyDecoratedDescriptor(_class2.prototype, "business", [_dec8, _dec9], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor4 = _applyDecoratedDescriptor(_class2.prototype, "validity", [_dec10, _dec11, _dec12], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
}), _descriptor5 = _applyDecoratedDescriptor(_class2.prototype, "categories", [_dec13, _dec14, _dec15, _dec16, _dec17], {
  configurable: true,
  enumerable: true,
  writable: true,
  initializer: null
})), _class2)) || _class);

The actual code itself imports modules from an all-entities.ts file, which exports all the entities in the correct order so that superclasses don’t accidentally get loaded after their subclasses, causing errors. That file looks like this (simplified):

export { default as Qualification } from '../entities/Qualification';
export { default as Business } from '../entities/Business';

./entities/Qualification.ts and ./entities/Business.ts are both files that contain a default export of a TypeORM entity, and I don’t feel like they’re worth including here but I can if anyone wants to look at them. Here’s the difference between my production and development webpack configs (generated by Next.js):

diff --git a/webpack-config-dev.txt b/webpack-config-prod.txt
index f8a28c3..8e5fa4d 100644
--- a/webpack-config-dev.txt
+++ b/webpack-config-prod.txt
@@ -1,80 +1,82 @@
 {
   externals: [ [Function] ],
   optimization: {
     checkWasmTypes: false,
     nodeEnv: false,
     splitChunks: false,
     runtimeChunk: undefined,
     minimize: false,
     minimizer: [ [TerserPlugin], [CssMinimizerPlugin] ]
   },
   context: 'C:\Users\Robbie\Code\fit-society',
   node: { setImmediate: false },
   entry: [AsyncFunction: entry],
   output: {
     path: 'C:\Users\Robbie\Code\fit-society\.next\server',
     filename: [Function: filename],
     libraryTarget: 'commonjs2',
     hotUpdateChunkFilename: 'static/webpack/[id].[hash].hot-update.js',
     hotUpdateMainFilename: 'static/webpack/[hash].hot-update.json',
-    chunkFilename: '[name].js',
+    chunkFilename: '[name].[contenthash].js',
     strictModuleExceptionHandling: true,
     crossOriginLoading: undefined,
-    futureEmitAssets: false,
+    futureEmitAssets: true,
     webassemblyModuleFilename: 'static/wasm/[modulehash].wasm'
   },
   performance: false,
   resolve: {
     extensions: [
       '.tsx',  '.ts',
       '.js',   '.mjs',
       '.jsx',  '.json',
       '.wasm'
     ],
     modules: [ 'node_modules' ],
     alias: {
       'next/head': 'next/dist/next-server/lib/head.js',
       'next/router': 'next/dist/client/router.js',
       'next/config': 'next/dist/next-server/lib/runtime-config.js',
       'next/dynamic': 'next/dist/next-server/lib/dynamic.js',
       next: 'C:\Users\Robbie\Code\fit-society\node_modules\next',
       'private-next-pages': 'C:\Users\Robbie\Code\fit-society\src\pages',
       'private-dot-next': 'C:\Users\Robbie\Code\fit-society\.next'
     },
     mainFields: [ 'main', 'module' ],
     plugins: [ [Object] ]
   },
   resolveLoader: {
     alias: {
       'emit-file-loader': 'C:\Users\Robbie\Code\fit-society\node_modules\next\dist\build\webpack\loaders\emit-file-loader',
       'error-loader': 'C:\Users\Robbie\Code\fit-society\node_modules\next\dist\build\webpack\loaders\error-loader',
       'next-babel-loader': 'C:\Users\Robbie\Code\fit-society\node_modules\next\dist\build\webpack\loaders\next-babel-loader',
       'next-client-pages-loader': 'C:\Users\Robbie\Code\fit-society\node_modules\next\dist\build\webpack\loaders\next-client-pages-loader',
       'next-data-loader': 'C:\Users\Robbie\Code\fit-society\node_modules\next\dist\build\webpack\loaders\next-data-loader',
       'next-serverless-loader': 'C:\Users\Robbie\Code\fit-society\node_modules\next\dist\build\webpack\loaders\next-serverless-loader',
       'noop-loader': 'C:\Users\Robbie\Code\fit-society\node_modules\next\dist\build\webpack\loaders\noop-loader',
       'next-plugin-loader': 'C:\Users\Robbie\Code\fit-society\node_modules\next\dist\build\webpack\loaders\next-plugin-loader'
     },
     modules: [ 'node_modules' ],
     plugins: [ [Object] ]
   },
   module: {
     rules: [ [Object], [Object], [Object] ],
     strictExportPresence: true
   },
   plugins: [
     ChunkNamesPlugin {},
     DefinePlugin { definitions: [Object] },
-    UnlinkRemovedPagesPlugin { prevAssets: {} },
-    NoEmitOnErrorsPlugin {},
-    NextJsRequireCacheHotReloader { prevAssets: null },
+    HashedModuleIdsPlugin { options: [Object] },
+    IgnorePlugin {
+      options: [Object],
+      checkIgnore: [Function: bound checkIgnore]
+    },
     PagesManifestPlugin { serverless: false },
     NextJsSsrImportPlugin { options: [Object] },
     NextJsSsrImportPlugin {},
     FilterWarningsPlugin { exclude: [Array] }
   ],
-  mode: 'development',
+  mode: 'production',
   name: 'server',
   target: 'node',
-  devtool: 'cheap-module-source-map'
+  devtool: false
 }

Here are the classes that are causing the problem:
Business.ts:

import {
  ArrayMaxSize,
  ArrayMinSize,
  IsArray,
  IsIn,
  IsOptional,
  IsString,
  IsUrl,
  MaxLength,
  ValidateNested,
  IsEmail
} from 'class-validator';
import { Column, CreateDateColumn, Entity, OneToMany } from 'typeorm';
import { CATEGORIES } from '../../misc-types/category';
import { Geolocation } from '../../misc-types/geolocation';
import { Account, BaseOffer, Qualification, ValidateableQualification } from '../db/all-entities';
import { omit } from './utils/entity-type-manipulations';
import tuple from './utils/string-enum-from-tuple';

const BUSINESS_TYPES = tuple('individual', 'company');
const PRICING_PLANS = tuple('free');

@Entity()
export default class Business extends Account {
  /**
   * Public name for the business.
   */
  @Column()
  @IsString()
  name!: string;

  @Column({ type: 'enum', enum: BUSINESS_TYPES })
  @IsIn(BUSINESS_TYPES)
  type!: typeof BUSINESS_TYPES[number];

  @Column()
  isApproved!: boolean;

  /**
   * The date the business created their account (not when it was approved)
   */
  @CreateDateColumn()
  since!: Date;

  @Column({ nullable: true })
  @IsOptional()
  @IsUrl()
  logoUrl?: string;

  @Column({ nullable: true })
  @IsOptional()
  @IsString()
  fein?: string;

  @Column({ nullable: true })
  @IsOptional()
  @IsString()
  phoneNumber?: string;

  @Column()
  @IsString()
  @MaxLength(200)
  bio!: string;

  @OneToMany(
    type => Qualification,
    qualification => qualification.business,
    { cascade: true }
  )
  @ValidateNested()
  qualifications!: Qualification[];

  @Column('simple-json')
  @IsArray()
  @ValidateNested()
  @IsIn(
    CATEGORIES.filter(c => c.type === 'business').map(c => c.slug),
    { each: true }
  )
  businessCategories!: string[];

  /**
   * Places this business is available at
   */
  @Column('simple-json')
  @IsArray()
  @ValidateNested()
  @ArrayMinSize(1)
  @ArrayMaxSize(5)
  geolocations!: Geolocation[];

  @Column({ type: 'enum', enum: PRICING_PLANS })
  @IsIn(PRICING_PLANS)
  pricingPlan!: 'free';

  @OneToMany(
    type => BaseOffer,
    offer => offer.offerer
  )
  offers!: BaseOffer[];
}

/**
 * A DTO sent to change business properties, most of which align one-to-one (excluding password/passwordHash).
 */
export class EditableBusiness extends omit(Business, [
  'id',
  'since',
  'isApproved',
  'qualifications',
  'passwordHash',
  'offers'
]) {
  @IsString()
  password!: string;
}

export class BusinessApplication extends EditableBusiness {
  @ValidateNested()
  @IsOptional()
  initialQualification!: ValidateableQualification;
}

And in Qualification.ts:

import { IsJSON, IsUrl, ValidateNested, IsOptional, IsBoolean, IsIn, IsArray } from 'class-validator';
import { Column, Entity, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Business } from '../db/all-entities';
import tuple from './utils/string-enum-from-tuple';
import { omit } from './utils/entity-type-manipulations';
import { CATEGORIES } from '../../misc-types/category';

const VALIDITY_STATES = tuple('valid', 'pending-review', 'invalid');

@Entity()
export default class Qualification {
  @PrimaryGeneratedColumn()
  id!: number;

  @Column({ nullable: true })
  // businesses do not need image proof
  @IsOptional()
  @IsUrl()
  imageUrl?: string;

  @ManyToOne(
    type => Business,
    business => business.qualifications,
    { onDelete: 'CASCADE' }
  )
  business!: Business;

  @Column({ type: 'enum', enum: VALIDITY_STATES, default: 'invalid' })
  @IsIn(VALIDITY_STATES)
  validity!: typeof VALIDITY_STATES[number];

  /**
   * The categories (slugs)
   */
  @Column('simple-json')
  @ValidateNested()
  @IsArray()
  @IsIn(
    CATEGORIES.filter(c => c.type === 'service').map(c => c.slug),
    { each: true }
  )
  categories!: string[];
}

/**
 * A qualification that can be sent by a business which is not necessarily verified yet.
 */
export const ValidateableQualification = omit(Qualification, ['id', 'business', 'validity']);
export type ValidateableQualification = typeof ValidateableQualification extends new () => infer U ? U : never;

Whenever either of these classes are imported, they’re imported from this file to ensure the proper module loading order:

/* eslint-disable import/first */
/**
 * This file exists to solve circular dependency problems with Webpack by explicitly specifying the module loading order.
 * @see https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de
 */

export { default as Qualification, ValidateableQualification } from '../entities/Qualification';

export { default as Account } from '../entities/Account';
export { default as Business, EditableBusiness, BusinessApplication } from '../entities/Business';
export { default as Customer } from '../entities/Customer';

export { default as BaseOffer } from '../entities/Offer';

import ProductOffer, { EditableProductOffer } from '../entities/ProductOffer';
import ServiceOffer, { EditableServiceOffer } from '../entities/ServiceOffer';

export { default as ProductOffer, EditableProductOffer } from '../entities/ProductOffer';
export { default as ServiceOffer, EditableServiceOffer } from '../entities/ServiceOffer';

export type Offer = ProductOffer | ServiceOffer;
export type EditableOffer = EditableProductOffer | EditableServiceOffer;

Babel is also used in this project. Here’s the .babelrc:

{
  "presets": [
    [
      "next/babel",
      {
        "class-properties": {
          "loose": true
        },
        "styled-jsx": {
          "plugins": [
            "styled-jsx-plugin-postcss"
          ]
        }
      }
    ]
  ],
  "plugins": [
    "babel-plugin-transform-typescript-metadata",
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ]
  ]
}

Sorry for the huge wads of code. Could anybody help me try to solve this and figure out how to make it work in production like it works in development? Thanks.

Solution

EDIT:
I’ve seen you’re using Qualification as a value in the ValidateableQualification. This sounds like something you can do in JS, but I think this will mess up with TS inheritance / compilation, since using Qualification as a value and not a type force TS to import the actual code while webpack bundling is done.

Furthermore, maybe you can do this with class-validator by itself or an extended class.

export const ValidateableQualification = omit(Qualification, ['id', 'business', 'validity']);

Can you try to remove this code, and see if circular dependency still rise?

Furthermore, I’ve read you are importing all entities from a central file. Even if this sounds like a good thing to resolve circular dependencies, it may lead to bugs since there is the chance you are going to import values instead of types. I suggest you to not use something like that to import entities between them, just use a central file like RelationalEntities.ts and do:

export const RelationalEntities = [
  Qualification,
  Business,
  // ...
]

and use this in TypeORM database connection configuration, i.e. entities: RelationalEntities

The old answer, before seeing updated code and config:

Usually, this is solved in TypeORM just using type => Type in the relationship definition, instead of true type Type. i.e.:

@OneToMany(type => Qualification)
qualification!: Qualification;

// instead of (will not work)
@OneToMany(Qualification)
qualification!: Qualification;

This is due to TS working, so that type => Qualification is essentially used only to extract metadata about Qualification, without referencing it at runtime (or, better, without referencing it directly at first run and so since it is lazy, no circular dependency is present)

Answered By: Anonymous

Related Articles

  • npm install error in vue
  • EntityMetadataNotFound: No metadata for…
  • Aurelia + Typescript + @bindable
  • error LNK2005: ✘✘✘ already defined in…
  • Improve INSERT-per-second performance of SQLite
  • Maven2: Missing artifact but jars are in place
  • UnsatisfiedDependencyException: Error creating bean…
  • easiest way to extract Oracle form xml format data
  • sklearn plot confusion matrix with labels
  • Create the perfect JPA entity
  • Error: container has not been made global - how to solve?
  • Ember service is undefined after being injected into…
  • What is a NullReferenceException, and how do I fix it?
  • CSS Float: Floating an image to the left of the text
  • Spring data jpa- No bean named…
  • Vue&TypeScript: how to avoid error TS2345 when…
  • Identifying and solving…
  • Entity Framework Provider type could not be loaded?
  • GLYPHICONS - bootstrap icon font hex value
  • Spring Boot - Cannot determine embedded database…
  • How to type multiple yield generator (redux-saga)
  • Connection Strings for Entity Framework
  • There is already an object named in the database
  • Getting a "TypeError" when trying to validate a form
  • How can I use/create dynamic template to compile…
  • typeORM combine next.js can not connect database with error
  • Issue with iron-ajax request
  • How to use Polymer 2 Build Process?
  • Ukkonen's suffix tree algorithm in plain English
  • Tomcat 7 "SEVERE: A child container failed during start"
  • Can't upload files with Apollo-client GraphQL in…
  • JUNIT @ParameterizedTest , Parameter resolution Exception
  • The 'compilation' argument must be an instance of…
  • How to loop over a Class attributes in Java?
  • Sort table rows In Bootstrap
  • How to pass type value to a variable with the type…
  • notifyDataSetChanged not working on RecyclerView
  • Component Inheritance with vue js
  • edgeID is returned as alpha numeric instead of long…
  • Iterator invalidation rules
  • Use of PUT vs PATCH methods in REST API real life scenarios
  • Spring: return @ResponseBody "ResponseEntity"
  • Exception in thread "main"…
  • java.lang.RuntimeException: Unable to instantiate…
  • Dynamically update values of a chartjs chart
  • Which maven dependencies to include for spring 3.0?
  • JSON Response Formatted Odd
  • What is the scope of variables in JavaScript?
  • Maven Could not resolve dependencies, artifacts…
  • Babel transpiling es7 class decorators Unexpected…
  • For-each over an array in JavaScript
  • Check if record exists from controller in Rails
  • Testing aurelia customElement with bindable and dependencies
  • Entity Framework and Connection Pooling
  • Next.js - Warning: Prop `dangerouslySetInnerHTML`…
  • How to tell Entity Framework to not include a nested object?
  • Launching Spring application Address already in use
  • Combining Ember Table with Ember Data
  • Creating a custom counter in Spark based on…
  • What are the undocumented features and limitations…
  • How can a button from an element call a function of…
  • I got an error at compile Nestjs with typeorm
  • When use getOne and findOne methods Spring Data JPA
  • Eclipse Spring Boot need declare Maven dependency explicitly
  • How can I mock an ES6 module import using Jest?
  • Exporting to .xlsx using…
  • How to reference and import reflect-metadata in…
  • Fastest way to iterate over all the chars in a String
  • Polymer Paper-Input not working or rendering
  • What's the difference between eval, exec, and compile?
  • The superclass "javax.servlet.http.HttpServlet" was…
  • NullpointerException error while working with…
  • How does PHP 'foreach' actually work?
  • How to create a class of classes
  • How to filter a RecyclerView with a SearchView
  • EntityFramework core - Update a collection of data…
  • What's the best way to get the last element of an…
  • Disable Logback in SpringBoot
  • How to register component in vue+ts correctly?
  • How can I pass modelformset_factory validation in Django?
  • How do SO_REUSEADDR and SO_REUSEPORT differ?
  • Spring Boot with ElasticSearch in Groovy: WebClient…
  • Unset WooCommerce checkout fields based on cart…
  • Ember.js ember-data and cross-domain ajax requests
  • Eclipse will not start and I haven't changed anything
  • aurelia-bootstrapper not found after upgrading to jspm beta
  • What is the difference between persist() and merge()…
  • Error creating bean with name 'entityManagerFactory'…
  • "Non-resolvable parent POM: Could not transfer…
  • The model backing the 'ApplicationDbContext' context…
  • A default parent router with child routers
  • for typescript the error for 'decorators-legacy'…
  • Smart way to truncate long strings
  • Exception in thread "JobGenerator"…
  • using d3.js with aurelia framework
  • What is null in Java?
  • How to observe embedded arrays in Ember objects?
  • java.lang.ClassNotFoundException:…
  • Can't correctly import external JS into Aurelia…
  • Change private static final field using Java reflection

Disclaimer: This content is shared under creative common license cc-by-sa 3.0. It is generated from StackExchange Website Network.

Post navigation

Previous Post:

Creating new elements at runtime using Mithril

Next Post:

Get data-attribute of paper-checkbox on on-tap event

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

.net ajax android angular arrays aurelia backbone.js bash c++ css dataframe ember-data ember.js excel git html ios java javascript jquery json laravel linux list mysql next.js node.js pandas php polymer polymer-1.0 python python-3.x r reactjs regex sql sql-server string svelte typescript vue-component vue.js vuejs2 vuetify.js

  • you shouldn’t need to use z-index
  • No column in target database, but getting “The schema update is terminating because data loss might occur”
  • Angular – expected call-signature: ‘changePassword’ to have a typedeftslint(typedef)
  • trying to implement NativeAdFactory imports deprecated method by default in flutter java project
  • What should I use to get an attribute out of my foreign table in Laravel?
© 2022 Fix Code Error