typeorm: 0.3 generic findOneBy typescript error

Issue Description

With the upgrade to the new syntax in 0.3 there is a typescript issue with using findOneBy on a generic repro.

export abstract class BaseEntity {
  abstract readonly id: string
}

export async function checkAuth<T extends BaseEntity>(
  entity: T,
  repo?: Repository<T>,
): Promise<[T, boolean]> {
  let existingEntity: T | null

  let exists = false
  if (repo) {
    existingEntity = await repo.findOneBy({ id: entity.id })

Expected Behavior

The code above should compile fine, no issues

Actual Behavior

I get the following typescript error.

TS2345: Argument of type '{ id: string; }' is not assignable to parameter of type 'FindOptionsWhere<T> | FindOptionsWhere<T>[]'.   Types of property 'id' are incompatible.     Type 'string' is not assignable to type 'FindOptionsWhereProperty<NonNullable<T["id"]>> | undefined'.
  • ✖️ 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, but I can support (using donations) development.
  • ✖️ No, I don’t have the time and I’m okay to wait for the community / maintainers to resolve this issue.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 18
  • Comments: 34

Commits related to this issue

Most upvoted comments

This way works fine:

import { FindOptionsWhere } from 'typeorm';

export class BaseController<Entity extends { id: string }> {
...
    async one(request: Request, response: Response, next: NextFunction) {
        return this.repository.findOneBy({ id: request.params.id } as FindOptionsWhere<Entity>);
    }
...
}

I have this issue as well and can’t figure out how to solve it without some nasty casting. The issues I encountered while using generics even before 0.3 makes me think nobody on the dev team is using TypeORM with generic entities.

Same here, issue is still unresolved

I’m having the same issue. I build find options first and then conditionally change it based on some things. Solved it for now by using BaseEntity first and then casting back to FindManyOptions

  private async getInstalls<T extends MyBaseEntity>(
    entity: new () => T,
    packageName: string,
    period: Period,
    from: Date,
  ): Promise<T[]> {
    const repo = this.manager.getRepository<T>(entity);

    const findOptions: FindManyOptions<MyBaseEntity> = { where: { packageName } };
    switch (period) {
      case Period.Day:
        findOptions.where = Object.assign(findOptions.where, { date: this.toDate(from) });
        break;
      // ...
    }

    return repo.find(findOptions as FindManyOptions<T>);
  }

This issue still exists on 0.3.10. Reopen?

Unfortunately, that is not true. Still getting the same error.

ScreenShot 04-28 at 09 41

Good news guys. After spend 10 hours got the solutions. I just bump up my TypeScript version and it just worked

Why is this issue closed? Still need to cast FindWhereOptions to solve the issue, btw thx for the solution @Metanart ❤️

this is my temporary way 😦, hope typeorm will resolve soon

const findOptions: FindOneOptions<Entity> = {
      where: {
        uuid,
      } as unknown as FindOptionsWhere<Entity>,
      ...(options || {}),
    };
    const data = await this.findOne(findOptions);

issue remains in 0.3.11 and typescript 4.9.4. casting FindWhereOptions is the workaround but not the solution

Same issue with v 0.3.11-dev.cdabaa3. Please reopen this

@paramsinghvc Your error message seems to be different to the one being discussed here, where it is about generic types.

See this codesandbox with the code from the original issue: https://codesandbox.io/s/typescript-playground-export-forked-hiqfd0

If you copy the code to your own code and edit the types, you will see that the issue still persists with the code in your commit, so that would unfortunately not fix this issue. I also think it’s the same with the other issue you mentioned.

You should open a new issue and include the code that you used to encounter the problem you have.

I’m facing the same issue. After some digging I found that it’s happening due to the faulty typings

/**
 * A single property handler for FindOptionsWhere.
 */
export declare type FindOptionsWhereProperty<Property> =
  Property extends Promise<infer I>
    ? FindOptionsWhereProperty<NonNullable<I>>
    : Property extends Array<infer I>
    ? FindOptionsWhereProperty<NonNullable<I>>
    : Property extends Function
    ? never
    : Property extends Buffer
    ? Property | FindOperator<Property>
    : Property extends Date
    ? Property | FindOperator<Property>
    : Property extends ObjectID
    ? Property | FindOperator<Property>
    : Property extends object
    ?
        | FindOptionsWhere<Property>
        | FindOptionsWhere<Property>[]
        | EqualOperator<Property>
        | FindOperator<any>
        | boolean
    : Property | FindOperator<Property>;
/** :
 * Used for find operations.
 */
export declare type FindOptionsWhere<Entity> = {
  [P in keyof Entity]?: FindOptionsWhereProperty<Entity[P]>;
};

Error says:

(property) id?: string | FindOperator<string>
Argument of type '{ id: string; }' is not assignable to parameter of type 'FindOptionsWhere<User>'.
  Types of property 'toString' are incompatible.
    Type '() => string' is not assignable to type 'never'.ts(2345)

which points out the typing done as Property extends Function ? never. If a property is a function type, it shouldn’t be assigned to never.

TypeOrm version: ^0.3.7