EntityManager create() implies either strictPropertyInitialization=false or optional auto-generated properties
It seems that TypeDORM assumes/requires Typescripts strictPropertyInitialization to be set false. It is automatically set to true if the strict option is used in tsconfig.
The pattern for create() requires the passing of an instantiated object, which is not possible if auto-generated properties are non-optional in the entity definition. If the attributes are to be auto-generated, they cannot be known in advance. TypeORM therefore seems to mandate either:
-
strictPropertyInitialization = falsewhich affects the entire project for all types which decreases type safety - Auto-generated properties to be marked as optional, again reducing type safety as class properties could then be set to undefined, contrary to the class/interface definition
A simple solution to this is to provide an overload to create() that allows for the entity class and a partial entity to be passed as arguments. e.g:
function create<
Entity,
PartialEntity = Partial<Entity>
>(
entityClass: Entity,
draft: PartialEntity,
options?: EntityManagerCreateOptions,
metadataOptions?: MetadataOptions
): Promise<Entity>
This would allow usage such as:
// assume @Entity decorator
class Foo {
@AutoGenerateAttribute({ strategy: AUTO_GENERATE_ATTRIBUTE_STRATEGY.UUID4 })
id: string
@Attribute()
bar: string
constructor(other: Foo) {
this.id = other.id
this.bar = other.bar
}
}
getEntityManager().create(Todo, { bar: 'hello' })
create() would now be responsible for creating the entity instance that is returned, and that might add a requirement, like in the example, for there to be a constructor that accepts an object).
Whilst the simplistic Partial<Entity> doesn't enforce all of the required properties of the class, the signature allows the user to supply a specific interface that does.
As it stands, I can't see how one would avoid the paradox of needing to create an instance of an entity without first knowing the values for non-optional, auto-generated members, unless strictPropertyInitialization is mandatory. Looking at the source for create(), there is a property key check to see if a property needs to be auto-generated, and therefore the property key must be missing for auto-generation to occur (which mandates either strictPropertyInitialization or optional auto-generated attributes).
Requiring the user to have optional auto-generated attributes adds significant burden elsewhere in the code to avoid the possibility of undefined, and requiring strictPropertyInitialization = false weakens type safety project wide.
I ran into this myself, and while I appreciate the proposal in theory, as written it doesn't have the type safety that I'd prefer. Fortunately, it's not necessary, as Typescript gives us the feature we need with the declare keyword:
@Entity({
name: 'organisation',
// etc.
})
export class Organisation
{
@AutoGenerateAttribute({
strategy: AUTO_GENERATE_ATTRIBUTE_STRATEGY.UUID4,
})
// `delcare` tells typescript that this property exists and can be referenced
// without requiring initialization
// `readonly` tells typescript not to allow writes to the property, if auto-
// generated ids should be considered permanent
declare readonly id: string;
// etc.
}
This form will allow your id field to be non-optional (avoiding annoying ? or undefined checks) but eliminates the need to initialize it in the constructor (which interferes with auto-generation). I recommend that the docs be updated with this as the standard form for auto-generated fields.