Skip to main content
Version: 6.4

属性验证

必需属性

¥Required properties

默认情况下,实体属性被视为必需的,因此,它们将在类型级别和运行时级别进行验证。要使属性可空,我们需要在类型级别和元数据级别对其进行标记(除非我们使用 ts-morph 进行元数据反射):

¥Entity properties are by default considered as required, and as such, they will be validated on both type level and runtime level. To make a property nullable, we need to mark this both at type level and at metadata level (unless we are using ts-morph for metadata reflection):

@Property({ nullable: true })
name?: string;

如果我们想使用显式 null,我们还应该提供属性初始化程序:

¥In case we want to use explicit null, we should also provide property initializer:

@Property({ type: 'string', nullable: true })
name: string | null = null;

具有默认值的属性

¥Properties with default value

如果你所需的属性具有默认值,则运行时验证将正常工作。但是,当涉及到类型验证时,我们需要对 TypeScript 进行额外的提示,以便它理解我们的属性(对于 TS 定义为必需)实际上也是可选的(因为我们有一个默认值)。我们有两个选项:

¥The runtime validation will work fine if your required properties have default value. But when it comes to type validation, we will need additional hint for TypeScript, so it understands our property (for TS defined as required) is in fact optional too (because we have a default value for it). We have two options:

将属性定义为可选,即使它具有默认值(并不完美,因为它将允许取消设置此类属性,这可能不是你想要的):

¥Define property as optional even when it has a default value (not perfect as it will allow unsetting such property, which might not be what you want):

@Property({ default: 1 })
level?: number = 1;

或者使用 OptionalProps 符号,专门设计用于帮助此用例。它应该被定义为实体上的可选属性,并且其类型需要是你想要标记为可选的所有属性的联合。

¥Or use OptionalProps symbol, specially designed to help with this use case. It should be defined as optional property on the entity and its type needs to be a union of all the properties you want to mark as optional.

import { OptionalProps, Entity, PrimaryKey, Property } from '@mikro-orm/core';

@Entity()
class User {

// getters will have the same problem
[OptionalProps]?: 'foo' | 'bar' | 'fooBar';

@PrimaryKey()
id!: number;

@Property({ default: 1 })
foo: number = 1;

@Property({ default: 2 })
bar: number = 2;

@Property({ persist: false })
get fooBar() {
return foo + bar;
}

}

当你想在自己的基本实体类中定义一些可选属性时,请使用泛型,这样你就可以从扩展类中添加更多属性:

¥When you want to define some optional properties in your own base entity class, use generics, so you can add more properties from the extending classes:

@Entity()
class MyBaseEntity<Entity extends object, Optional extends keyof Entity = never> {

[OptionalProps]?: 'foo' | 'bar' | Optional;

@PrimaryKey()
id!: number;

@Property({ default: 1 })
foo: number = 1;

@Property({ default: 2 })
bar: number = 2;

}

@Entity()
class User extends MyBaseEntity<User, 'baz'> {

@Property({ default: 3 })
baz: number = 3;

}

另一种方法是使用 Opt 类型,它可以以两种方式使用:

¥An alternative approach is using the Opt type, which can be used in two ways:

  • 使用泛型:middleName: Opt<string> = '';

    ¥with generics: middleName: Opt<string> = '';

  • 使用交集:middleName: string & Opt = '';

    ¥with intersections: middleName: string & Opt = '';

两者的工作方式相同,并且可以与 OptionalProps 符号方法结合使用。

¥Both will work the same, and can be combined with the OptionalProps symbol approach.

import { Opt, Entity, PrimaryKey, Property } from '@mikro-orm/core';

@Entity()
class User {

@PrimaryKey()
id!: number;

@Property()
firstName!: string;

@Property()
middleName: string & Opt = '';

@Property()
lastName!: string;

@Property({ persist: false })
get fullName(): Opt<string> {
return `${this.firstName} ${this.middleName} ${this.lastName}`;
}

}

运行时验证

¥Runtime validation

运行时验证发生在刷新操作上,就在我们触发插入查询之前。

¥The runtime validation happens on flush operation, right before we fire the insert queries.

如果出于某种原因,我们不希望 ORM 抛出标记为必需的缺失属性,我们可以通过 validateRequired: false 禁用验证。自 v5 以来,此验证默认启用。

¥If for some reason we don't want the ORM to throw on missing properties that are marked as required, we can disable the validation via validateRequired: false. This validation is enabled by default since v5.

关于可选属性和元数据的说明反射

¥Note about optional properties and metadata reflection

当我们定义实体时,我们需要谨慎使用可选属性。使用 reflect-metadata 提供程序(默认提供程序),只有通过 ? 后缀将属性标记为可选时,才能推断属性类型 - 如果我们使用像 string | null 这样的类型联合,reflect-metadata 将无法帮助我们处理这种类型,我们将被迫明确定义它。ts-morph 提供程序不存在此问题。

¥When we define our entities, we need to be careful about optional properties. With reflect-metadata provider (the default one), the property type can be inferred only if we mark properties as optional via ? suffix - if we would use a type union like string | null, reflect-metadata won't help us with such type, and we will be forced to define it explicitly. This issue is not present with ts-morph provider.

严格属性类型验证

¥Strict property type validation

自 v4 以来,你需要确保使用正确类型的 validate: true 才能访问特定于驱动程序的方法(如 )。它会影响性能,通常不需要,只要你不通过 Object.assign() 修改实体即可。

¥Since v4.0.3 the validation needs to be explicitly enabled via validate: true. It has performance implications and usually should not be needed, as long as you don't modify your entities via Object.assign().

MikroORM 会在实际持久化发生之前验证你的属性。它会尝试自动为你修复错误的数据类型。如果自动转换失败,它将引发错误。你可以启用严格模式来禁用此功能,并让 ORM 抛出错误。持久化实体时会触发验证。

¥MikroORM will validate your properties before actual persisting happens. It will try to fix wrong data types for you automatically. If automatic conversion fails, it will throw an error. You can enable strict mode to disable this feature and let ORM throw errors instead. Validation is triggered when persisting the entity.

// number instead of string will throw
const author = new Author('test', 'test');
wrap(author).assign({ name: 111, email: 222 });
await orm.em.persist(author).flush(); // throws "Validation error: trying to set Author.name of type 'string' to '111' of type 'number'"

// string date with unknown format will throw
wrap(author).assign(author, { name: '333', email: '444', born: 'asd' });
await orm.em.persist(author).flush(); // throws "Validation error: trying to set Author.born of type 'date' to 'asd' of type 'string'"

// string date with correct format will be auto-corrected
wrap(author).assign({ name: '333', email: '444', born: '2018-01-01' });
await orm.em.persist(author).flush();
console.log(author.born).toBe(true); // instance of Date

// Date object will be ok
wrap(author).assign({ born: new Date() });
await orm.em.persist(author).flush();
console.log(author.born).toBe(true); // instance of Date

// null will be ok
wrap(author).assign({ born: null });
await orm.em.persist(author).flush();
console.log(author.born); // null

// string number with correct format will be auto-corrected
wrap(author).assign({ age: '21' });
await orm.em.persist(author).flush();
console.log(author.age); // number 21

// string instead of number with will throw
wrap(author).assign({ age: 'asd' });
await orm.em.persist(author).flush(); // throws "Validation error: trying to set Author.age of type 'number' to 'asd' of type 'string'"
wrap(author).assign({ age: new Date() });
await orm.em.persist(author).flush(); // throws "Validation error: trying to set Author.age of type 'number' to '2019-01-17T21:14:23.875Z' of type 'date'"
wrap(author).assign({ age: false });
await orm.em.persist(author).flush(); // throws "Validation error: trying to set Author.age of type 'number' to 'false' of type 'boolean'"