假设我有两个实体:车辆和关税。它们是一对多的关系(车辆可以有很多关税)。
这是我对对象的基本类型定义,我想使用:
interface Tariff {
id: string; // <-- ID of entity
createdAt: Date; // <-- some meta data fields for sorting and etc.
days: number; // <-- userful info
price: number; // <-
}
interface Vehicle {
id: string; // <-- ID of entity
createdAt: Date; // <-- some meta data fields for sorting and etc.
model: string; // <-- userful info
year?: string; // <-
seatsCount?: number; // <-
tariffs: Tariff[]; // <-- array of tariffs
}
看起来很棒。不,我们需要创建数据库模型。首先让我们定义猫鼬模式:
import mongoose from "mongoose";
const vehicleSchema = new mongoose.Schema({
createdAt: {type: Date, required: true, default: () => new Date()},
model: {type: String, required: true},
year: String,
seatsCount: Number,
});
太棒了。我决定不将关税放入车辆文件中,它们将单独存储。
接下来,我们需要创建猫鼬模型。但是我们需要提供将mongoose.Document
扩展到mongoose.model
的通用类型。这是代码复制开始的地方:
// First we need to define TypeScript schema equalent of mongoose schema:
interface VehicleSchema {
createdAt: Date;
model: string;
year?: string;
seatsCount?: number;
}
// Then we need to defined a new type of out vehicle models:
type VehicleModel = VehicleSchema && mongoose.Document;
// And finaly we can create model:
const VehicleModel = mongoose.model<VehicleModel>("Car", carSchema);
// Look, here is a type named "VehicleModel" and a constant named "VehicleModel"
现在我们可以编写用于CRUD操作的函数了:
namespace vehicle {
export const create = async (form: VehicleCreateForm): Promise<Vehicle> => {
const fields = toDb(form);
// assume form is prevalidated
let dbVehicle = new VehicleModel(form);
dbVehicle = await model.save();
// tariff - is the separate module, like this one,
// createAllFor methods retuns Tariff[]
const tariffs = await tariff.createAllFor(dbVehicle.id, form.tariffs);
return fromDb(dbVehicle, tariffs);
}
//...
// export const update = ...
// export const list = ...
// export const get = ...
// etc..
}
在此我们引入一种额外的类型:VehicleCreateForm
,它描述了创建载具所需的所有字段:
interface VehicleCreateForm {
model: string;
year?: string;
seatsCount?: number;
tariffs: TariffCreateFields[]; // <-- here is another one special type
}
interface TariffCreateFields {
days: number;
price: number;
}
我们还需要定义两个函数:toDb
以防止出现某些字段,例如tariffs
被传递给模型。然后fromDb
将模型“转换”为Vehicle
实体,删除或转换某些字段:
const u = <T>(value: T | undefined | null): (T | undefined) => value === null ? undefined : value;
const toDb = (f: VehicleCreateForm): VehicleCreateFields => ({
model: f.model,
year: f.year,
seatsCount: f.seatsCount,
});
const fromDb = (m: VehicleModel, tariffs: Tariff[]): Vehicle => ({
id: m.id,
createdAt: m.createdAt,
model: m.model,
year: u(m.year),
tariffs,
});
而且,yaaa,我们还需要一种额外的类型:VehicleCreateFields
。我们要传递给模型构造函数的字段。
interface VehicleCreateFields {
model: string;
year?: string;
seatsCount?: number;
}
似乎完成了。
我们还需要定义类似于tariff
的{{1}}命名空间。所有类型,并且也会重复:vehicle
,TariffSchema
,TariffModel
,TariffCreateForm
。
这将在每个新实体中发生。在当前情况下,我可以避免创建TariffCreateDocument
类型,而可以使用VehicleUpdateFields
进行创建和更新。可能还有VehicleCreateFields
等。
如何减少重复代码的数量。您如何处理打字稿/猫鼬?
当然,我可以将公共字段提取到“公共chunck”接口,但是我不知道如何命名它们。