Skip to content

Commit

Permalink
feat(core): return Loaded type from Ref.load()
Browse files Browse the repository at this point in the history
Closes #3755
  • Loading branch information
B4nan committed Nov 5, 2023
1 parent 0ef4132 commit bc3ffa9
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 4 deletions.
10 changes: 6 additions & 4 deletions packages/core/src/entity/Reference.ts
Expand Up @@ -2,8 +2,10 @@ import { inspect } from 'util';
import type {
ConnectionType,
Dictionary,
EntityClass, EntityKey,
EntityClass,
EntityProperty,
Loaded,
EntityKey,
LoadedReference,
Populate,
Primary,
Expand Down Expand Up @@ -97,7 +99,7 @@ export class Reference<T extends object> {
* Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
* Returns the entity.
*/
async load<K extends keyof T = never, P extends string = never>(options?: LoadReferenceOptions<T, P>): Promise<T>;
async load<TT extends T, P extends string = never>(options?: LoadReferenceOptions<T, P>): Promise<Loaded<TT, P>>;

/**
* Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
Expand All @@ -109,7 +111,7 @@ export class Reference<T extends object> {
* Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
* Returns either the whole entity, or the requested property.
*/
async load<K extends keyof T = never, P extends string = never>(options?: LoadReferenceOptions<T, P> | K): Promise<T | T[K]> {
async load<TT extends T, K extends keyof T = never, P extends string = never>(options?: LoadReferenceOptions<T, P> | K): Promise<Loaded<TT, P> | T[K]> {
const opts: Dictionary = typeof options === 'object' ? options : { prop: options };

if (!this.isInitialized()) {
Expand All @@ -120,7 +122,7 @@ export class Reference<T extends object> {
return this.entity[opts.prop as EntityKey];
}

return this.entity;
return this.entity as Loaded<TT, P>;
}

set(entity: T | Ref<T>): void {
Expand Down
36 changes: 36 additions & 0 deletions tests/types.test.ts
Expand Up @@ -579,6 +579,42 @@ describe('check typings', () => {
const res: Loaded<MemberNotification> | null = await em.findOne('MemberNotification' as EntityName<MemberNotification>, {} as MemberNotification | string);
});

test('Ref.load() returns Loaded type (#3755)', async () => {
interface Parent {
id: number;
children: Collection<Child>;
}

interface Child {
id: number;
parent: Ref<Parent>;
}

const parent = {} as Ref<Parent>;

// @ts-expect-error Loaded<Parent, never> is not assignable
const populated01: Loaded<Parent, 'children'> = {} as Loaded<Ref<Parent>>;
// @ts-expect-error Loaded<Parent, never> is not assignable
const populated02: Loaded<Parent, 'children'> = {} as Loaded<Parent>;
let populated1: Loaded<Parent, 'children'>;
function foo(e: Loaded<Parent, 'children'>) {
//
}
const e = await parent.load();
// @ts-expect-error Loaded<Parent, never> is not assignable
foo(e);
// populated1 = await parent.load();
// @ts-expect-error Loaded<Parent, never> is not assignable
const populated2: Loaded<Parent, 'children'> = await parent.load({ populate: [] });

// only this should pass
const populated3: Loaded<Parent, 'children'> = await parent.load({ populate: ['children'] });
const populated4: Loaded<Parent, 'children'> = await parent.load({ populate: ['children', 'id'] });

// this fails because of https://github.com/mikro-orm/mikro-orm/issues/3277
// const populated5: Loaded<Parent, 'children'> = await parent.load({ populate: ['children.parent'] });
});

test('exclusion', async () => {
interface Notification {
id: string;
Expand Down

0 comments on commit bc3ffa9

Please sign in to comment.