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

FindOne where property allows using of Entity properties that don't exist #5832

Closed
araujoigor opened this issue Apr 8, 2020 · 6 comments
Closed
Labels

Comments

@araujoigor
Copy link

araujoigor commented Apr 8, 2020

Issue type:

[ ] question
[x] bug report
[ ] feature request
[ ] documentation issue

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

Assume I have the following entity:

@Entity()
class EntityA {
    @PrimaryGeneratedColumn()
    id: number;

    @Column("text")
    name: string;
}

And want to find one entry:

const model: EntityA = await connection
    .getRepository(EntityA)
    .findOne({
        where: {
            id: 1,
            name: 'name'
            propertyIDontHave: true,
        },
    });

I don't get a Typescript error for propertyIDontHave because the type of the where property on the FindOneOptions interface has ObjectLiteral on its union type definition

Why is is the case? Shouldn't the where type be restrictive to the properties for the entity being used?

In case the type relaxing here is done on purpose, shouldn't there be a way to enforce proper type checking?

I know that this was already kind of discussed (on a different point of view) on #3416. Although I feel like this is a bit different, I'm happy to having this closed in favor of that one.

My suggestion here would be to create a whereRaw of type string|ObjectLiteral and make the regular where property strictly a FindConditions<Entity>

Thanks!

@arekbal
Copy link

arekbal commented Apr 9, 2020

To enforce type checking I am using helpers using more strict types, a bit like you suggest.
Very useful with selects for instance.
Have a look:

export function findOneSelect<TEntity extends ObjectLiteral, TSelectKey extends keyof TEntity>(
  repo: RepoFindOne<TEntity>,
  opts: FindOneOptions<TEntity>,
  select: TSelectKey[]
): Promise<Pick<TEntity, TSelectKey>> {
  return repo.findOne(Object.assign(opts, { select }))
}

It is not a where example but I hope you get the basic logic behind it.

You don't always want to use the helper as there are more exotic queries you might want to do with typeorm, but for most plain queries, it is much cleaner to use such helper.

Also, mind you, you can declare your own limited subtype from Repository<TEntity>, compatible with the Repository... a bit like I did in aforementioned example - RepoFindOne exposes only findOne method, but I use FindOneOptions<T> which I might replace with something more strict in the future(edit: just replaced in my code).
So you would have something like

function processQuery(repo: RepoFindOne<DuduEntity>) {
//... with your limited - more typesafe - method signature
return repo.findOne({ where: { wouldnTWorkWithNonExistentProp: true } })
}
const repo: Repository<DuduEntity> = x
processQuery(repo)

@ejose19
Copy link
Contributor

ejose19 commented May 17, 2020

Is there a way to override provided declarations in .d.ts files so we can adjust to our needs on typings folder?

Edit: For anyone interested, since I couldn't override .d.ts from outside, I used https://github.com/ds300/patch-package to manually patch included files without having to host a copy of the repo. Hopefully on 0.3 this won't be an issue.

@brt-han-mai
Copy link

typeorm is supposed to check the types of the queries at the compiled time (at least, if we use it to build simple queries like this)
I think you should change this to a "bug report" where they will not ignore your question.

@araujoigor
Copy link
Author

@brt-han-mai fully agree with you and updated the issue type. Let's see if it yields any results.

@ejose19
Copy link
Contributor

ejose19 commented Sep 23, 2020

For those who needs this behaviour now, this is the patch diff you can use along patch-package (it only changes type definitions)

diff --git a/node_modules/typeorm/find-options/FindOneOptions.d.ts b/node_modules/typeorm/find-options/FindOneOptions.d.ts
index dc1155e..880fe11 100644
--- a/node_modules/typeorm/find-options/FindOneOptions.d.ts
+++ b/node_modules/typeorm/find-options/FindOneOptions.d.ts
@@ -1,6 +1,7 @@
 import { JoinOptions } from "./JoinOptions";
 import { ObjectLiteral } from "../common/ObjectLiteral";
 import { FindConditions } from "./FindConditions";
+
 /**
  * Defines a special criteria to find specific entity.
  */
@@ -12,7 +13,7 @@ export interface FindOneOptions<Entity = any> {
     /**
      * Simple condition that should be applied to match entities.
      */
-    where?: FindConditions<Entity>[] | FindConditions<Entity> | ObjectLiteral | string;
+    where?: FindConditions<Entity>[] | FindConditions<Entity> | string;
     /**
      * Indicates what relations of entity should be loaded (simplified left join form).
      */
diff --git a/node_modules/typeorm/query-builder/SelectQueryBuilder.d.ts b/node_modules/typeorm/query-builder/SelectQueryBuilder.d.ts
index 60e49fe..6967efa 100644
--- a/node_modules/typeorm/query-builder/SelectQueryBuilder.d.ts
+++ b/node_modules/typeorm/query-builder/SelectQueryBuilder.d.ts
@@ -11,6 +11,7 @@ import { QueryRunner } from "../query-runner/QueryRunner";
 import { WhereExpression } from "./WhereExpression";
 import { Brackets } from "./Brackets";
 import { SelectQueryBuilderOption } from "./SelectQueryBuilderOption";
+import { FindConditions } from '../find-options/FindConditions';
 /**
  * Allows to build complex sql queries in a fashion way and execute those queries.
  */
@@ -361,7 +362,7 @@ export declare class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> imp
      * calling this function will override previously set WHERE conditions.
      * Additionally you can add parameters used in where expression.
      */
-    where(where: Brackets | string | ((qb: this) => string) | ObjectLiteral | ObjectLiteral[], parameters?: ObjectLiteral): this;
+    where(where: Brackets | string | ((qb: this) => string) | FindConditions<Entity> , parameters?: ObjectLiteral | Entity): this;
     /**
      * Adds new AND WHERE condition in the query builder.
      * Additionally you can add parameters used in where expression.

@imnotjames imnotjames added the bug label Oct 6, 2020
@imnotjames
Copy link
Contributor

We have a plan to correct this in a future release - once we migrate to TS4.0+ - until then it's very difficult to get the typings right across all edge cases. There's a few other issues open for this so I'm going to close this one.

There's now runtime errors that are emitted but we can't start work on the compile time errors working until we get TS4.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants