У меня есть несколько сущностей TypeORM в моей кодовой базе, которые имеют отношения друг к другу, образуя круговую зависимость. Поскольку метаданные декоратора используются в каждом классе сущностей, TypeScript вставляет код после каждого класса, определяющего метаданные для него. Скажите, что классы Business
и Qualification
. В соответствующих полях TypeScript выдаст код, который выглядит следующим образом:
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);
Все это будет хорошо, за исключением того, что часть __decorate
всегда идет после каждого класса, что означает, что один из классов будет иметь использоваться до его определения, что вызывает ошибку. Вот сокращенная версия фактического кода с фактической ошибкой:
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);
Позже в коде определено Business
, но уже слишком поздно:
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);
Как ни странно, код работает при компиляции для режима разработки, поскольку на Business
ссылаются не напрямую, а через константу модуля. Вот как Qualification
определяется в режиме разработки:
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);
Фактический код сам импортирует модули из файла all-entities.ts
, который экспортирует все сущности в правильном порядке, чтобы суперклассы не могли случайно получить загружаются после их подклассов, вызывая ошибки. Этот файл выглядит следующим образом (упрощенно):
export { default as Qualification } from '../entities/Qualification';
export { default as Business } from '../entities/Business';
./entities/Qualification.ts
и ./entities/Business.ts
- оба файла, которые содержат экспорт по умолчанию сущности TypeORM, и я не думаю, что их стоит включать здесь, но я могу, если кто-то хочет посмотреть на них. Вот разница между моими конфигами веб-пакетов производства и разработки (сгенерированными 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
}
Вот классы, которые вызывают проблему: 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;
}
И в 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;
Всякий раз, когда любой из этих классов импортируется, они импортируются из этого файла для обеспечения правильного порядка загрузки модуля:
/* 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 также используется в этом проекте. Вот что такое .babelr c:
{
"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
}
]
]
}
Извините за огромные комки кода. Может кто-нибудь помочь мне попытаться решить эту проблему и выяснить, как заставить это работать в производстве, как это работает в разработке? Спасибо.