You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When using the repositories' find(options?: FindManyOptions<Entity>): Promise<Entity[]>; with passed relations, the typing does not know that these properties exist now.
asyncfunctiongetUser(repo: Repository<User>){constusers=awaitrepo.find({relations: {group: true}});constuser=users[0];user.group;// might be undefined according to type}
The Solution
In our project, I created the following solution:
First, we need to ensure that we have the relations information (e.g. {group: true}) as a type by defining it as a template (TRelations)
Transform that type using the Entity and Relations and remove undefined from these properties
The following example showcases the typings in a current typeorm project:
The type ResolveRelations works in the following way:
// This works in the following way:// 1. The result is `TEntity & TResolveResult`, ie the Entity is always present// 2. TResolveResult contains all keys of the TRelations objects // 3. For each Property in TRelations, remove undefined// 4. If the property is an object, recursively call ResolveRelationsexporttypeResolveRelations<TRelationsextendsFindOptionsRelations<TEntity>,TEntity,>=nullextendsTEntity
? (TEntity&ResolveRelationsImpl<TRelations,TEntity>)|null
: TEntity&ResolveRelationsImpl<TRelations,TEntity>;// Helper to allow indexing an object even if it is nulltypeNullableIndex<TObject,TPropextendskeyofNonNullable<TObject>>=nullextendsTObject
? NonNullable<TObject>[TProp]|null
: NonNullable<TObject>[TProp];// The right side of `TEntity & TResolveResult` typeResolveRelationsImpl<TRelationsextendsFindOptionsRelations<TEntity>,TEntity>={[PinkeyofTRelations]: PextendskeyofNonNullable<TEntity>
? TRelations[P]extendsobject
? ResolveRelations<TRelations[P],Exclude<NullableIndex<TEntity,P>,undefined>>
: Exclude<NullableIndex<TEntity,P>,undefined>
: never;};
Note that I did not include the case of passing false to the relation and it probably needs a lot of work to make it work with legacy options.
Considered Alternatives
We could also use different models and interfaces for our User. One interface with populated groups, one without, etc. A major downside is that the type system does not guarantee that there wasn't any typo. Using the above approach, I get typescript errors if I mistakenly tried to populated createdByUser instead of deletedByUser.
Additional Context
I think drivers are irrelevant as it is affecting typescript only.
Relevant Database Driver(s)
aurora-mysql
aurora-postgres
better-sqlite3
cockroachdb
cordova
expo
mongodb
mysql
nativescript
oracle
postgres
react-native
sap
spanner
sqlite
sqlite-abstract
sqljs
sqlserver
Are you willing to resolve this issue by submitting a Pull Request?
No, I don’t have the time and I’m okay to wait for the community / maintainers to resolve this issue.
The text was updated successfully, but these errors were encountered:
Feature Description
When using
find()
(and friends) withrelations
, I'm always frustrated that the returned type does not have the resolved relations.Consider the following Entities (decorators omitted for brevity)
When using the repositories'
find(options?: FindManyOptions<Entity>): Promise<Entity[]>;
with passed relations, the typing does not know that these properties exist now.The Solution
In our project, I created the following solution:
{group: true}
) as a type by defining it as a template (TRelations
)The following example showcases the typings in a current typeorm project:
The type
ResolveRelations
works in the following way:Note that I did not include the case of passing
false
to the relation and it probably needs a lot of work to make it work with legacy options.Considered Alternatives
We could also use different models and interfaces for our
User
. One interface with populated groups, one without, etc. A major downside is that the type system does not guarantee that there wasn't any typo. Using the above approach, I get typescript errors if I mistakenly tried to populatedcreatedByUser
instead ofdeletedByUser
.Additional Context
I think drivers are irrelevant as it is affecting typescript only.
Relevant Database Driver(s)
Are you willing to resolve this issue by submitting a Pull Request?
No, I don’t have the time and I’m okay to wait for the community / maintainers to resolve this issue.
The text was updated successfully, but these errors were encountered: