diff --git a/packages/core/src/entity/EntityFactory.ts b/packages/core/src/entity/EntityFactory.ts index 5796bb04eb56..23c8e2622c52 100644 --- a/packages/core/src/entity/EntityFactory.ts +++ b/packages/core/src/entity/EntityFactory.ts @@ -384,7 +384,19 @@ export class EntityFactory { } if (!meta.properties[k]) { - return data; + const tmp = { ...data }; + + for (const prop of meta.props) { + if (prop.customType && tmp[prop.name] != null) { + tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.platform) as any; + } + } + + return tmp; + } + + if (meta.properties[k].customType && data[k] != null) { + return meta.properties[k].customType!.convertToJSValue(data[k], this.platform); } return data[k]; diff --git a/packages/core/src/utils/Utils.ts b/packages/core/src/utils/Utils.ts index 60dd4ad01c72..b772a6e458fb 100644 --- a/packages/core/src/utils/Utils.ts +++ b/packages/core/src/utils/Utils.ts @@ -480,7 +480,7 @@ export class Utils { if (inside === 2 && token.type === 'Punctuator' && token.value === '{') { ret.push(ObjectBindingPattern as unknown as string); - i += 2; + i = tokens.findIndex((t, idx) => idx > i + 2 && t.type === 'Punctuator' && t.value === '}'); continue; } diff --git a/tests/features/custom-types/GH5118.test.ts b/tests/features/custom-types/GH5118.test.ts new file mode 100644 index 000000000000..75bea11f3421 --- /dev/null +++ b/tests/features/custom-types/GH5118.test.ts @@ -0,0 +1,95 @@ +import { Entity, MikroORM, PrimaryKey, Property, Type } from '@mikro-orm/sqlite'; + +class Value { + + protected readonly value: string; + + toString() { + return this.value; + } + + constructor(id: string) { + this.value = id; + } + +} + +class SimpleType extends Type< + Value | undefined, + string | undefined +> { + + constructor(private classRef: new (value: any) => Value) { + super(); + } + + convertToDatabaseValue( + value: Value | string | null | undefined, + ): string | undefined { + if (!value) { + return undefined; + } + if (typeof value === 'string') { + return value; + } + return value.toString(); + } + + convertToJSValue( + value: Value | string | undefined, + ): Value | undefined { + if (!value) { + return undefined; + } + return new this.classRef( + typeof value === 'object' ? value.toString() : value, + ); + } + +} + +@Entity() +class File { + + @PrimaryKey({ type: new SimpleType(Value) }) + readonly id: Value; + + @Property({ type: new SimpleType(Value) }) + readonly uri: Value; + + constructor({ id, uri }: { id: Value; uri: Value }) { + this.id = id; + this.uri = uri; + } + +} + +let orm: MikroORM; + +beforeAll(async () => { + orm = await MikroORM.init({ + forceEntityConstructor: true, + entities: [File], + dbName: ':memory:', + }); + await orm.schema.createSchema(); +}); + +afterAll(async () => { + await orm.close(true); +}); + +test(`custom types and forceEntityConstructor`, async () => { + await orm.em.fork().persistAndFlush( + new File({ + id: new Value('foo'), + uri: new Value('bar'), + }), + ); + + const retrieved = await orm.em.findOneOrFail(File, { + id: new Value('foo'), + }); + expect(retrieved.id).toBeInstanceOf(Value); + expect(retrieved.uri).toBeInstanceOf(Value); +});