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

TypeError: Cannot convert object to primitive value #2065

Closed
shashikiran797 opened this issue May 2, 2018 · 19 comments · Fixed by #6884
Closed

TypeError: Cannot convert object to primitive value #2065

shashikiran797 opened this issue May 2, 2018 · 19 comments · Fixed by #6884

Comments

@shashikiran797
Copy link

Issue type:

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

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[ ] mysql / mariadb
[ ] oracle
[x] postgres
[ ] sqlite
[ ] sqljs
[ ] react-native

TypeORM version:

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

Steps to reproduce or a small repository showing the problem:

I am using NestJs with Typeorm
I get the request object form graphql, graphql parameters do not have __proto__ set.
I am directly passing the graphql paramter object to the save method. I am getting the below error when I do that:

TypeError: Cannot convert object to primitive value
    at new MustBeEntityError (/Users/shashikiranms/projects/vcbackend/src/error/MustBeEntityError.ts:10:43)
    at EntityPersistExecutor.execute (/Users/shashikiranms/projects/vcbackend/node_modules/typeorm/persistence/EntityPersistExecutor.js:77:35)
    at EntityManager.save (/Users/shashikiranms/projects/vcbackend/src/entity-manager/EntityManager.ts:298:14)
    at UserRepository.Repository.save (/Users/shashikiranms/projects/vcbackend/src/repository/Repository.ts:138:29)

When I debugged the issue, it is beacause
EntityPersistExecutor.js checks for the entity to be an instanceof Object, which is false, because __proto__ is not set for the entity

if (!this.entity || !(this.entity instanceof Object)) {
     return Promise.reject(new MustBeEntityError_1.MustBeEntityError(this.mode, this.entity));
}

I think changing the above condition this.entity instanceof Object to typeof this.entity === 'object' solves the problem

PS: It was working in version 0.1.20.
It is breaking once I upgraded to 0.2.0

@pleerock
Copy link
Member

pleerock commented May 2, 2018

typeof null also is an object, if value you have isn't instance of Object then what it is?

@shashikiran797
Copy link
Author

shashikiran797 commented May 3, 2018

this.entity is not null but an object, but it does not inherit from Object (i.e this.entity.__proto__ is undefined) that's why this.entity instanceof Object is false.

The request object I get from graphQL is being created by Object.create(null);, hence instanceof Object is false in my case

Please refer to the conversation in the below pull request, where they are using Object.create(null) to create an object:
graphql/graphql-js#504

@pleerock
Copy link
Member

pleerock commented May 4, 2018

okay.. are you able to provide a PR?

@abrahamx97
Copy link

abrahamx97 commented Oct 10, 2018

@shashikiran797 how do you solve this problem? I have the same problem using Nestjs with typescript, graphql and typeorm
I have typeorm version 0.2.7

@shashikiran797
Copy link
Author

One way to solve this problem is to have constructors to each entity, create objects of entity class and pass them to the tyoeorm methods

@vlapo
Copy link
Contributor

vlapo commented Dec 4, 2018

Sure this.entity !== null && typeof this.entity === 'object' is satisfactory solution of this issue but this condition is true also for new String(), new Number(), new Boolean(), new Date(), new Error(), ... (more: http://bonsaiden.github.io/JavaScript-Garden/#types.typeof).

For javascript support is better condition Object.prototype.toString.call({}) === '[object Object]'. But there are some performance differences and also we have to check array (maybe type of each item in array if we want to be perfect).

"toString.call"                    x  93,991,941 ops/sec ±1.03% (86 runs sampled)
"toString.call + Array.isArray     x  82,102,219 ops/sec ±1.58% (89 runs sampled)
"typeof'"                          x 687,506,864 ops/sec ±0.71% (90 runs sampled)
"instanceof"                       x 116,683,504 ops/sec ±1.44% (88 runs sampled)
Fastest is "typeof"

Note: run on node 10 on my personal pc

What do you think @pleerock? Better type check for javascript or just fast bugfix using typeof?

@shayded-exe
Copy link
Contributor

Any news on this? I'm also having issues because of this when using GraphQL with TypeORM.

@KovalenkoYurii
Copy link

KovalenkoYurii commented Jun 26, 2019

@shashikiran797 @PachowStudios Hello, I'd stucked with same problem. I found to instantinate entity with repository.create method of typeorm before saving and save the new instance to db. Example:
public createBun(newBun: DeepPartial<Bun>): Promise<Bun> { const bun = this.bunRepository.create(newBun); return this.bunRepository.save(bun); }

@ricky300300
Copy link

You can solve something like this

action.additionalDetails = {}; if(input.additionalDetails) { Object.assign(action.additionalDetails, input.additionalDetails); }

@meteorlxy
Copy link

Any news on that?

Many other projects are trying to create Objects by Object.create(null), which results in [Object: null prototype].

However, there are so many instanceof Object in typeorm, which may cause a lot of issues.

Object.create(null) instanceof Object // false

Hope we can fix that, or at least put a notice in the documentation.

@SkvokeN
Copy link

SkvokeN commented Sep 9, 2019

Hello, any news?

@brokenthorn
Copy link

I don't know if my issue #4955 is related to this one but if it is, maybe someone can better explain the global issue here and why Typeorm needs prototype based inheritance?

@brokenthorn
Copy link

brokenthorn commented Oct 23, 2019

One way to solve this problem is to have constructors to each entity, create objects of entity class and pass them to the tyoeorm methods

Won't work if the library you're using like Type-GraphQL creates proto-less objects when passing arguments to mutations for example and you use those same objects to pass to Typeorm to save or update entities. Typeorm probably needs to switch to proto-less objects too since it only uses them as hashmaps (anyway, right?).

@amille14
Copy link

Just ran into this issue using type-graphql, typeorm and nestjs and was scratching my head for a while. This is a really obtuse issue and causes silent bugs. There are workarounds but it really needs to be fixed.

@jpoppe
Copy link

jpoppe commented Jan 9, 2020

I solved it for my use case in NestJS with a simple object spread (required for input):

@Mutation(() => Item)               
public createItem(@Args('input') input: ItemCreateInput): Promise<Item>         
  return this.itemsService.create({...input}) // spread operator to clone input to new object                            
 } 

Hope it helps..

@ArcaneDiver
Copy link

probably we could just create a sort of Pipe that re-instance the object.

@greenreign
Copy link

Just ran into this issue with nestjs and graphql during a find condition and I'm shocked I haven't run into it before. Or have I?

@maayanbar13
Copy link
Contributor

I've had the same issue with the mongodb driver as well, with the same use case - nestjs + graphql + typeorm while using the same models as the input... can anyone offer a workaround that is more generic then what @jpoppe suggested?

maayanbar13 added a commit to maayanbar13/typeorm that referenced this issue Oct 10, 2020
imnotjames pushed a commit that referenced this issue Oct 12, 2020
Due to `instanceof` comparison, object without prototype is mistaken for a privative value, throwing `TypeError: Cannot convert object to primitive value.` when trying to `.toString` said object (inside template string).

This mostly effects usage with `graphql` as `graphql-js`, [by design](graphql/graphql-js#504), creates objects by using Object.create(null).
This fix allows saving these objects.    

The fix uses `typeof` instead of `toString` comparison since I see no reason why `new Number/Boolean/Date` should be a supported use case.

Closes: #2065
zaro pushed a commit to zaro/typeorm that referenced this issue Jan 12, 2021
Due to `instanceof` comparison, object without prototype is mistaken for a privative value, throwing `TypeError: Cannot convert object to primitive value.` when trying to `.toString` said object (inside template string).

This mostly effects usage with `graphql` as `graphql-js`, [by design](graphql/graphql-js#504), creates objects by using Object.create(null).
This fix allows saving these objects.    

The fix uses `typeof` instead of `toString` comparison since I see no reason why `new Number/Boolean/Date` should be a supported use case.

Closes: typeorm#2065
@siberian-man
Copy link

siberian-man commented Mar 14, 2021

I use NestJS (GraphQL) and TypeORM. To bypass the issue I created a global pipe as shown bellow:

import { PipeTransform } from '@nestjs/common/interfaces';

class EnsurePrototypePipe implements PipeTransform {
  transform(argument: unknown) {
    return ensurePrototype(argument);
  }
}

const ensurePrototype = (input: unknown): unknown => {
  if (Array.isArray(input)) {
    return input.map(it => ensurePrototype(it));
  }
  if (isObjectLiteral(input) && input.prototype === undefined) {
    const output = {};
    Object.keys(input).forEach(property => {
      output[property] = ensurePrototype(input[property]);
    });
    return output;
  }
  return input;
};

export const isObjectLiteral = (v: unknown): v is Record<string | number | symbol, unknown> => {
  if (typeof v !== 'object' || v == null) {
    return false;
  }
  const constructor = v.constructor;
  return constructor === undefined || constructor === Object;
};

And apply this pipe globally:

<...>
nestApp.useGlobalPipes(new EnsurePrototypePipe())
<...>

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

Successfully merging a pull request may close this issue.