Handle Undefined Object in `isInternalEntity`
JoseRFelix opened this issue · comments
Problem
When adding a non-entity in a relation property, the following error occurs:
TypeError: Cannot use 'in' operator to search for '__type' in null
This also breaks dropping a db
.
Expected Outcome
The code should not throw because of a null object, but error or warn the user about him using a normal object instead of an entity for a relation.
Hy, @JoseRFelix. Thanks for raising this.
This is a valid concern and I agree that the library should warn/error when attempting to assign a plain compatible object as the relational property. This looks like a partial follow-up of #93.
Have just stumbled upon this issue. Do I understand correctly that the following thing is not possible at the moment:
const db = factory({
project: {
id: primaryKey(String),
name: String,
owner: oneOf('user'),
},
user: {
username: primaryKey(String),
name: String,
},
})
[
{ username: 'username1', name: 'Jack' },
{ username: 'username2', name: 'John' }
].forEach(user => {
db.user.create(user)
})
[
{ id: '1', name: 'Project 1', owner: 'username1' },
{ id: '2', name: 'Project 2', owner: 'username2' }
].forEach(project => {
const owner = db.user.findFirst({ where: { username: { equals: project.owner } } })
db.project.create({ ...project, owner })
})
because findFirst
method returns plain object and not an entity, right?
Interesting, that when I run this code i get this TypeError: Cannot use 'in' operator to search for '__type' in null
not every time, but once in a while.
Hm... seems like even you do it like in docs
const db = factory({
project: {
id: primaryKey(String),
name: String,
owner: oneOf('user'),
},
user: {
username: primaryKey(String),
name: String,
},
})
const user = db.user.create({ username: 'username1', name: 'Jack' })
const project = db.project.create({ id: '1', name: 'Project 1', owner: user })
You can still get this error once in a while
@smashercosmo, if I'm not mistaken, assigning plain objects as relational values isn't supported at the moment. It doesn't error, but won't be treated as a node reference, as opposed to when you assign the result of db[model].create()
. This is raised in #92 as well, where updating a relational property with a plain object doesn't update the references. I didn't have time to get to this, so I'd appreciate any thoughts/brainstorming on how to make this happen and will support the effort as much as I can given the time constraints.
Ok, but that's another issue, I guess. Currently there is an error raised occasionally even if you assign a result of db[model].create()
. Seems like there is some racing condition going on. Does db[model].create()
happens synchronously?
Currently, if I do this
const user = db.user.create({ username: 'username1', name: 'Jack' })
const project = db.project.create({ id: '1', name: 'Project 1', owner: user })
then once in 3-5 refreshes this error TypeError: Cannot use 'in' operator to search for '__type' in null
is being raised
Well, probably I'll just try to provide minimal reproduction.
Ok, I figured out what my issue was about. You get this error if you try to assign null
to any of the model's fields. Don't know, if it's a bug or by design. But can't think of any reason why null
can't be a valid value.
I've opened a pull request that'd ensure the value
passed to isInternalEntity
is always a defined object (#117). Passing anything else is unexpected and shouldn't happen. Since the values of the models are defined by the end-user, the function may still get an arbitrary value that violates its call signature.
@smashercosmo, using null
as the model property value is not correct. All model properties are nullable and don't have any value by default. There's no reason to use explicit null
. Instead, you should use a getter function that either resolves to an initial value ({ name: () => 'John' }
), or indicates the data type that can be inferred { name: String }
.
@kettanaito I'm not sure that model properties are nullable. At least typescript doesn't know about this.
const db = factory({
entity: {
id: primaryKey(datatype.uuid),
name: String,
}
})
const instance = db.entity.create({ name: null }) // <- TS2322: Type 'null' is not assignable to type 'Partial >> | undefined'.
@smashercosmo, I think at the moment you can set undefined
instead of null
. I'm open to discussion to support Vaule | null
instead.