Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy relations resolve to 'undefined' instead of 'null' #7146

Closed
4 of 21 tasks
securedirective opened this issue Dec 3, 2020 · 1 comment
Closed
4 of 21 tasks

Lazy relations resolve to 'undefined' instead of 'null' #7146

securedirective opened this issue Dec 3, 2020 · 1 comment

Comments

@securedirective
Copy link
Contributor

securedirective commented Dec 3, 2020

Issue Description

Expected Behavior

After reading about the partial-update feature, where a literal null is required to save NULL to the database, and undefined indicates the field should be skipped entirely... I expected similar behavior when reading the NULL back from the database: a Javascript null in all cases.

Specifically, I expected to be able to define my nullable field as Category | null and guarantee that the value will only ever be a Category instance or a null.

Actual Behavior

Lazy relations that are not in the 'relations' find option return a Promise that resolves to a Javascript undefined. The other scenarios return a null.

Since Typescript is unaware of this unexpected behavior, there are no compile-time errors. But this inconsistency causes problems later when the actual value of the variable does not match the type specification, so we have to use Category | null | undefined all over the place to account for both possibilities.

While I don't have much of a preference of null vs. undefined, the method of retrieval (lazy-loaded vs lazy-loaded but included in the 'relations' find option vs. eager-loaded) should not change the outcome. That inconsistency is what I'd consider a bug.

Steps to Reproduce

This is a somewhat-contrived example, just to illustrate:

@Entity()
export class Category {
    @PrimaryGeneratedColumn()
    id!: number;
}

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

    @ManyToOne(type => Category, { eager: true })
    eagerCategory!: Category | null;

    @ManyToOne(type => Category)
    lazyCategory!: Promise<Category | null>;
}
function verifyCategoryIsNull(category: Category | null) {
  if (category === null) {
    console.log('Oops! Post has no category');
  } else {
    console.log(`Awesome. Post has a category: ${category}`);
  }
}

let post: Post;
post = new Post();
await connection.manager.save(post);

// Completely lazy
post = await connection.manager.findOneOrFail(Post);
verifyCategoryIsNull(await post.lazyCategory);

// Lazy by definition, but marked for eager-loading at the query level
post = await connection.manager.findOneOrFail(Post, { relations: ['lazyCategory'] });
verifyCategoryIsNull(await post.lazyCategory);

// Always eager, so no need for a Promise at all
post = await connection.manager.findOneOrFail(Post);
verifyCategoryIsNull(post.eagerCategory);

Here is the output:

Awesome. Post has a category: undefined
Oops! Post has no category
Oops! Post has no category

Yes, I am aware that I could just replace === with ==, since that happens to match both null and undefined. Or I could do if (!category) and accept that it also matches other stuff. But a prominent purpose for using Typescript in the first place is to implement strong typing across the codebase. Ending up with a value that doesn't match the type specification is a problem.

My Environment

Dependency Version
Operating System Ubuntu
Node.js version v15.3.0
Typescript version v3.6.5
TypeORM version v0.2.29

Additional Context

I am using strict mode in Typescript.

Relevant Database Driver(s)

I demonstrated the failure on the following drivers, though it likely affects all of them; I don't think this is a driver-specific issue.

  • aurora-data-api
  • aurora-data-api-pg
  • better-sqlite3
  • cockroachdb
  • cordova
  • expo
  • mongodb
  • mysql
  • nativescript
  • oracle
  • postgres
  • react-native
  • sap
  • sqlite
  • sqlite-abstract
  • sqljs
  • sqlserver

Are you willing to resolve this issue by submitting a Pull Request?

  • Yes, I have the time, and I know how to start.
  • Yes, I have the time, but I don't know how to start. I would need guidance.
  • No, I don't have the time, although I believe I could do it if I had the time...
  • No, I don't have the time and I wouldn't even know how to start.

I've already forked the repo, fixed the issue, and prepared a PR and unit suite to hilight the issue. Working on some final touches, and I'll post when that is complete.

securedirective added a commit to securedirective/typeorm that referenced this issue Dec 3, 2020
…ypeorm#7146)

Add units tests to hilight when lazy relation resolves to 'undefined', instead of 'null' as
documentation implies.
securedirective added a commit to securedirective/typeorm that referenced this issue Dec 3, 2020
…ypeorm#7146)

Add units tests to hilight when lazy relation resolves to 'undefined', instead of 'null' as
the documentation implies.
securedirective added a commit to securedirective/typeorm that referenced this issue Dec 3, 2020
…null' (typeorm#7146)

Fix an issue where a lazy relation returns a Promise that resolves to 'undefined', when (as implied
by documentation) a NULL in the database should translate to a literal 'null' in Javascript. Also
added units tests to hilight the three scenarios in which this occurred.
securedirective added a commit to securedirective/typeorm that referenced this issue Dec 3, 2020
…ve no results (typeorm#7146)

Fix an issue where a lazy relation returns a Promise that resolves to 'undefined', when (as implied
by documentation) a NULL in the database should translate to a literal 'null' in Javascript. Also
added units tests to hilight the three scenarios in which this occurred.
securedirective added a commit to securedirective/typeorm that referenced this issue Dec 3, 2020
…ve no results (typeorm#7146)

Fix an issue where a lazy relation returns a Promise that resolves to 'undefined'. As implied by
documentation, a NULL in the database should instead translate to a literal 'null' in Javascript.
Also added units tests to hilight the three scenarios in which this occurred.
securedirective added a commit to securedirective/typeorm that referenced this issue Dec 3, 2020
…ve no results (typeorm#7146)

Fix an issue where a lazy relation returns a Promise that resolves to 'undefined'. As implied by
documentation, if the database has a NULL or no results, the result should be a literal 'null'
in Javascript. Also added units tests to hilight the three scenarios in which this occurred.

Closes typeorm#7146
@securedirective
Copy link
Contributor Author

Pull Request is complete, and all checks have passed: #7147

Ready to be merged in, if you all approve.

zshipleyTAG pushed a commit to Amherst-Development/typeorm that referenced this issue Oct 7, 2022
* typeorm-0.2.30: (212 commits)
  version bump
  docs: fix javascript usage examples (typeorm#7031)
  fix: resolve migration for UpdateDateColumn without ON UPDATE clause (typeorm#7057)
  fix: Error when sorting by an embedded entity while using join and skip/take (typeorm#7082)
  fix: resolves Postgres sequence identifier length error (typeorm#7115)
  feat: closure table custom naming (typeorm#7120)
  feat: relations: Orphaned row action (typeorm#7105)
  docs: fix invalid code block in "find many options" (typeorm#7268)
  docs: Embodying the example (typeorm#7116)
  docs: document withDeleted option (typeorm#7132)
  fix: return 'null' (instead of 'undefined') on lazy relations that have no results (typeorm#7146) (typeorm#7147)
  docs: update cascade options (typeorm#7140)
  docs: add .ts to supported ormconfig formats (typeorm#7139)
  fix: improve stack traces when using persist executor (typeorm#7218)
  refactor: remove Oracle multirow insert workaround (since typeorm#6927) (typeorm#7083)
  feat: add NOWAIT and SKIP LOCKED lock support for MySQL (typeorm#7236)
  docs: update OneToMany grammar (typeorm#7252)
  feat: JavaScript file migrations output (typeorm#7253)
  docs: update Repository.ts (typeorm#7254)
  chore: update dependency cli-highlight to v2.1.10 (typeorm#7265)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant