Skip to content

装饰器语法

基本概念

DataFaker为了更好的支持ts,引入了装饰器语法,装饰器语法本质上就是defineModel的语法糖,它设计的初衷就是为了保持现有类和模型的共通性。

基本使用

比如现在项目中本来就有UserAddress两个类作为 ts 类型

ts
class Address {
  declare city: string;
  declare children: Address[];
}
// 用户类
class User {
  declare id: string;
  declare firstName: string;
  declare secondName: string;
  declare age: number;
  declare email: string;
  declare address: Address;
  declare children: User[];
}

为了将这两个类利用起来,而不是重新使用defineModel来定义数据模型,我们可以使用装饰器语法来将现有的类型类定义为UserAddress数据模型。

  • 使用@DataModel装饰器定义数据模型,它接受一个模型别名作为参数
  • 使用@DataField装饰器来定义字段,与模板语法中定义字段是一致的

如下所示:

ts
@DataModel('address')
class Address {
  @DataField('location.city')
  declare city: string;
  @DataField({ refModel: 'address', count: 1, deep: 1 })
  declare children: Address[];
}
@DataModel('user')
class User {
  @DataField('string.uuid')
  declare id: string;
  @DataField('person.firstName')
  declare firstName: string;
  @DataField('person.lastName')
  declare secondName: string;
  @DataField(['number.int', { min: 18, max: 65 }])
  declare age: number;
  @DataField(ctx => {
    return faker.internet.email({ firstName: ctx.firstName, lastName: ctx.secondName });
  })
  declare email: string;
  @DataField({ refModel: 'address', count: 1 })
  declare address: Address;
  @DataField({ refModel: 'user', deep: 1, count: 1 })
  declare children: User[];
}
const userDatas = fakeData('user', 2);
console.dir(userDatas, { depth: Infinity });
ts
[
  {
    id: 'b8e8ade6-5f37-43d9-b512-4ba0395e5975',
    firstName: 'Cecile',
    secondName: 'MacGyver',
    age: 24,
    address: { city: 'Leviland', children: { city: 'North Georgianna' } },
    children: {
      id: 'f29ea63b-ac69-4832-9586-b82b17f2d40b',
      firstName: 'Floyd',
      secondName: 'Flatley',
      age: 57,
      address: { city: 'Lake Anissa', children: { city: 'North Beverlyshire' } },
      email: 'Floyd.Flatley@hotmail.com',
    },
    email: 'Cecile_MacGyver34@yahoo.com',
  },
  {
    id: '3647b033-470d-40f3-adf9-836df66f7eef',
    firstName: 'Evangeline',
    secondName: 'Kerluke',
    age: 23,
    address: { city: 'Raynorland', children: { city: 'West Rosetta' } },
    children: {
      id: '350c4642-761f-4b36-a6cf-5b1bcf35edcb',
      firstName: 'Aurelio',
      secondName: 'Kuvalis',
      age: 64,
      address: { city: 'Florence-Graham', children: { city: 'New Brock' } },
      email: 'Aurelio_Kuvalis61@yahoo.com',
    },
    email: 'Evangeline.Kerluke@yahoo.com',
  },
];

装饰器的本质

装饰器的本质就是defineModel的语法糖,它只是对类的定义进行了封装,并没有改变原有的类的结构和行为。

基于类继承的模型继承

DataFaker的强大远不止如此,在前面介绍过,如果要实现模型复用,我们可以克隆一个模型,然后再添加和删除其中的字段。比如像下面一样:

ts
const userModel = defineModel('user', {});
cloneModel('cloneUser', userModel).withProperty('age', { type: 'number.int', min: 18, max: 65 });

现在装饰器语法可以更加方便的实现模型继承,只需要像原生继承那样,无需做任何改动,如下所示,User 类从 Person 类的数据模型中继承了 emailchildren 字段:

ts
@DataModel('person')
class Person {
  @DataField(ctx => {
    return faker.internet.email({ firstName: ctx.firstName, lastName: ctx.secondName });
  })
  declare email: string;
  @DataField({ refModel: 'user', deep: 1, count: 1 })
  declare children: User[];
}
@DataModel('user')
class User extends Person {
  @DataField('string.uuid')
  declare id: string;
  @DataField('person.firstName')
  declare firstName: string;
  @DataField('person.lastName')
  declare secondName: string;
  @DataField(['number.int', { min: 18, max: 65 }])
  declare age: number;
}
const userDatas = fakeData('user', 2);
console.dir(userDatas, { depth: Infinity });
ts
[
  {
    children: {
      id: '01beb5dd-d2f8-4602-a4f6-4304d49b1532',
      firstName: 'Anjali',
      secondName: 'Murphy',
      age: 51,
      email: 'Anjali.Murphy@hotmail.com',
    },
    id: '041980c6-164a-4fad-81a2-65a3f9c64359',
    firstName: 'Kristy',
    secondName: 'Ledner',
    age: 62,
    email: 'Kristy_Ledner30@yahoo.com',
  },
  {
    children: {
      id: '2df47ecb-186e-4d9b-a417-4b62dd4906d0',
      firstName: 'Jody',
      secondName: 'Schmeler',
      age: 18,
      email: 'Jody_Schmeler@hotmail.com',
    },
    id: '26450cf7-f190-44dc-ab1b-6ff0faf8e74b',
    firstName: 'Nathanial',
    secondName: 'Schaden',
    age: 19,
    email: 'Nathanial.Schaden96@gmail.com',
  },
];

操作模型

有时候或许你会更想要操作数据模型,而不是基于类的模型继承,DataFaker 提供了useModel方法来获取原生模型,如下所示:

ts
@DataModel('person')
class Person {
  @DataField(ctx => {
    return faker.internet.email({ firstName: ctx.firstName, lastName: ctx.secondName });
  })
  declare email: string;
  @DataField({ refModel: 'person', deep: 1, count: 1 })
  declare children: User[];
}
@DataModel('user')
class User {
  @DataField('string.uuid')
  declare id: string;
  @DataField('person.firstName')
  declare firstName: string;
  @DataField('person.lastName')
  declare secondName: string;
  @DataField(['number.int', { min: 18, max: 65 }])
  declare age: number;
}
// 获取user模型和person模型
const userModel = useModel(User);
const personModel = useModel('person');
// 给user模型添加sex字段
userModel?.withProperties({
  sex: 'person.sex',
});
const userDatas = fakeData(userModel, 2);
const personDatas = fakeData(personModel, 2);
// 生成数据
console.dir(userDatas, { depth: Infinity });
console.dir(personDatas, { depth: Infinity });

结果如下:

基于原型的继承

或许你会想,直接操作userModel会不会导致personModel也发生变化,这里DataFaker已经为你考虑到了,数据模型的继承底层采用了基于原型的继承,基于属性屏蔽,你在userModel上添加的属性不会影响到personModel。所以你无需担心

ts
// userDatas
[
  {
    id: 'de3a6b97-a5e4-4486-9a24-4a37d4e73f40',
    firstName: 'Roel',
    secondName: 'Marks',
    age: 49,
    sex: 'female',
  },
  {
    id: '007224af-3495-4a3b-9eba-6456fca749d5',
    firstName: 'Yvette',
    secondName: 'Walker',
    age: 47,
    sex: 'male',
  },
][
  // personDatas
  ({
    children: { email: 'Zachery12@gmail.com' },
    email: 'Alessandra.Kohler@hotmail.com',
  },
  {
    children: { email: 'Eladio_Doyle@yahoo.com' },
    email: 'Shanny.Towne@yahoo.com',
  })
];