Skip to content

Commit

Permalink
fix(core): respect null in Loaded type
Browse files Browse the repository at this point in the history
Closes #2750
  • Loading branch information
B4nan committed Feb 12, 2022
1 parent 04d9d88 commit 72385b3
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 9 deletions.
13 changes: 7 additions & 6 deletions packages/core/src/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ export type EntityDataNested<T> = T extends undefined
type EntityDataItem<T> = T | EntityDataProp<T> | null;

type ExplicitlyOptionalProps<T> = T extends { [OptionalProps]?: infer PK } ? PK : never;
type ExplicitlyOptionalProps2<T> = ExplicitlyOptionalProps<T> | 'id' | '_id' | 'uuid';
type NullableKeys<T> = { [K in keyof T]: null extends T[K] ? K : never }[keyof T];
type ProbablyOptionalProps<T> = ExplicitlyOptionalProps<T> | 'id' | '_id' | 'uuid' | Defined<NullableKeys<T>>;

type IsOptional<T, K extends keyof T> = T[K] extends Collection<any>
? true
Expand All @@ -175,7 +176,7 @@ type IsOptional<T, K extends keyof T> = T[K] extends Collection<any>
? true
: K extends symbol
? true
: K extends ExplicitlyOptionalProps2<T>
: K extends ProbablyOptionalProps<T>
? true
: false;
type RequiredKeys<T, K extends keyof T> = IsOptional<T, K> extends false ? K : never;
Expand Down Expand Up @@ -606,13 +607,13 @@ type Defined<T> = Exclude<T, null | undefined>;
// 2. If no, just return it as-is (scalars will be included, loadables too but not loaded).
export type Loaded<T, L extends string = never> = T & {
[K in keyof T]: K extends Prefix<L>
? LoadedLoadable<Defined<T[K]>, Loaded<ExtractType<Defined<T[K]>>, Suffix<L>>>
? LoadedLoadable<T[K], Loaded<ExtractType<T[K]>, Suffix<L>>>
: T[K]
};

export interface LoadedReference<T> extends Reference<T> {
$: T;
get(): T;
export interface LoadedReference<T> extends Reference<Defined<T>> {
$: Defined<T>;
get(): Defined<T>;
}

export interface LoadedCollection<T> extends Collection<T> {
Expand Down
7 changes: 7 additions & 0 deletions tests/EntityManager.mongo2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ describe('EntityManagerMongo2', () => {
await expect(orm.em.persistAndFlush(jon)).rejects.toThrow(`Value for Author.email is required, 'undefined' found`);
});

test('Loaded type does not remove optionality', async () => {
const b = await orm.em.find(Book, {}, { populate: ['publisher'] });
// @ts-expect-error publisher can be null
const p1 = b[0]?.publisher.$.name;
const p2 = b[0]?.publisher?.$.name;
});

afterAll(async () => orm.close(true));

});
6 changes: 3 additions & 3 deletions tests/entities/Book.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ObjectId } from 'bson';
import type { EntityDTO } from '@mikro-orm/core';
import { Collection, IdentifiedReference, Cascade, Entity, Index, ManyToMany, ManyToOne, PrimaryKey, Property, Unique, wrap, Filter, Dictionary, OptionalProps } from '@mikro-orm/core';
import type { EntityDTO, IdentifiedReference } from '@mikro-orm/core';
import { Collection, Cascade, Entity, Index, ManyToMany, ManyToOne, PrimaryKey, Property, Unique, wrap, Filter, Dictionary, OptionalProps } from '@mikro-orm/core';
import { Publisher } from './Publisher';
import { Author } from './Author';
import { BookTag } from './book-tag';
Expand Down Expand Up @@ -33,7 +33,7 @@ export class Book extends BaseEntity3<Book> {

@ManyToOne(() => Publisher, { wrappedReference: true, cascade: [Cascade.PERSIST, Cascade.REMOVE], nullable: true })
@Index({ name: 'publisher_idx' })
publisher?: IdentifiedReference<Publisher, '_id' | 'id'>;
publisher!: IdentifiedReference<Publisher, '_id' | 'id'> | null;

@ManyToMany(() => BookTag)
tags = new Collection<BookTag>(this);
Expand Down

0 comments on commit 72385b3

Please sign in to comment.