Typescript 统一模型和模式
情境:Node.js中的Mongoose和VS代码中的Typescript 在我从模板继承的代码中,有以下Typescript代码:Typescript 统一模型和模式,typescript,mongoose,typescript-typings,Typescript,Mongoose,Typescript Typings,情境:Node.js中的Mongoose和VS代码中的Typescript 在我从模板继承的代码中,有以下Typescript代码: import mongoose from "mongoose"; export type UserModel = mongoose.Document & { firstName: string, lastName: string }; const UserSchema = new mongoose.Schema({ firstName: S
import mongoose from "mongoose";
export type UserModel = mongoose.Document & {
firstName: string,
lastName: string
};
const UserSchema = new mongoose.Schema({
firstName: String,
lastName: String
});
export const User = mongoose.model("User", UsersSchema);
以上工作。每当需要调用集合的find
或其他方法时,我都会使用User
。每当我需要指定回调参数的类型(例如)时,我就会使用UserModel
,以利用Intellisense和编译时属性检查。例如:
User.find((err, result: UserModel[])=>
{ console.log(result[0].lastName); }
但是,要确保UserModel
始终在镜像UsersSchema
,这是一项繁琐的工作。当有许多属性时,它会变得单调乏味
有没有一种更优雅的方法可以使用模式直接注释类型?Code
助手类型系统:
type SingleValueType<T> = Object extends T ? any :
T extends typeof mongoose.SchemaTypes.Mixed ? any :
T extends typeof Date ? Date :
T extends {(...args: any[]): infer R} ? R :
T extends {new (...args:any[]): infer R} ? R : never;
type ValueType<T> = T extends Array<infer R> ? Array<SingleValueType<R>> : SingleValueType<T>;
type DefaultType<T> = T extends {(...args: any[]): infer R} ? R : T;
type FieldDescriptionType<T> = [ValueType<T>] extends [never] ?
T extends {type: infer R, default: infer D} ? ValueType<R> | DefaultType<D> :
T extends {type: infer R} ? ValueType<R> : never
: ValueType<T>;
type FieldType<T> = [FieldDescriptionType<T>] extends [never] ?
T extends {[index: string]: any} ? docType<T> : never
: FieldDescriptionType<T>;
export type docType<T> = {
[P in keyof T]: FieldType<T[P]>
}
type SingleValueType=Object扩展T?任何:
T扩展mongoose.SchemaTypes.Mixed的类型?任何:
日期是多少?日期:
T扩展{(…args:any[]):推断R}?R:
T扩展{new(…args:any[]):推断R}?R:从来没有;
类型ValueType=T扩展数组?数组:SingleValueType;
type DefaultType=T扩展{(…args:any[]):推断R}?R:T;
类型FieldDescriptionType=[ValueType]扩展了[never]?
T扩展{type:inferr,默认值:inferd}?ValueType | DefaultType:
T扩展{type:inferr}?ValueType:从不
:ValueType;
类型FieldType=[FieldDescriptionType]扩展了[never]?
T扩展{[index:string]:any}?博士类型:从不
:FieldDescriptionType;
导出类型docType={
[P in keyof T]:字段类型
}
然后,我们可以使用最后一种类型,如下所示:
const userDoc = {
data: mongoose.Schema.Types.Mixed,
firstName: String,
lastName: {type: String},
index: Number,
oAuth: { provider: String, id: String, wrong: '' },
day: Date,
flag: Boolean,
itemNames: {type: [String], default: undefined},
itemIds: {type: [Number]}
};
export type UserModel = mongoose.Document & docType<typeof userDoc>;
const UserSchema = new mongoose.Schema(userDoc);
export const User = mongoose.model("User", UserSchema);
User.find((err, result: UserModel[])=> {
console.log(result[0].data); // any, since it's Mixed in mongoose (would be unknown in TS 3.0)
console.log(result[0].oAuth.provider); // string
result[0].oAuth.wrong = ''; // error, wrong is never
console.log(result[0].lastName); // string
console.log(result[0].day); // Date
console.log(result[0].flag); // boolean
console.log(result[0].itemNames); // string[] | undefined
console.log(result[0].itemIds); // number[]
});
const userDoc={
数据:mongoose.Schema.Types.Mixed,
名字:String,
姓氏:{type:String},
索引:编号,
oAuth:{provider:String,id:String,错误:“”,
日期:,
标志:布尔,
itemNames:{type:[String],默认值:undefined},
ItemId:{type:[Number]}
};
导出类型UserModel=mongoose.Document&docType;
const UserSchema=newmongoose.Schema(userDoc);
export const User=mongoose.model(“用户”,UserSchema);
User.find((错误,结果:UserModel[])=>{
console.log(结果[0].data);//任何,因为它在mongoose中是混合的(在TS 3.0中是未知的)
console.log(结果[0].oAuth.provider);//字符串
结果[0]。oAuth.error='';//错误,错误永远不会发生
console.log(结果[0].lastName);//字符串
console.log(结果[0].day);//日期
console.log(结果[0].flag);//布尔值
console.log(结果[0].itemNames);//字符串[]|未定义
console.log(结果[0].itemIds);//编号[]
});
解释 辅助对象类型由以下部分组成
SingleValueType
是一种允许我们通过大写形式(即按对象的原语)或按类的实例获取实际字段类型的类型;力学基于此类型,其本身分为以下部分:
- 首先,我们对
模式类型进行了特殊处理,因为它必须立即转换为混合
(在TS 2.9中)或任何
(在TS 3.0中)。它可以以两种不同的方式出现:要么显式声明,要么作为空对象文本;最后一个案例必须在这里处理,所以我们不会将其与子文档属性混合未知
- 接下来,我们对类型
进行了特殊处理,因为Date
为我们提供了newdate()
,但与大多数其他类不同的是,Date
Date()。所以,我们 只需明确说明它应该是什么
- 接下来,如果类型
(用作参数)是一个函数(即具有一些 调用签名),结果必须是此函数的返回类型。 这是从T
获取string
,string
从number
获取number
, 等等
- 接下来,如果
是构造函数类型(即具有T
签名),则 结果必须是此构造函数的返回类型,即实例 当然可以。这可用于存储任意复杂结构new
- 最后,如果前面的任何内容都不能使用,即
既不是 函数或构造函数类型,我们将类型设置为T
,这是唯一扩展never
本身的类型never
ValueType
和DefaultType
使用不同的转换,因为它们的设置不同ValueType
将其参数处理为类型本身(传递给SingleValueType
),或作为包含此类型的数组(因此它也为我们提供了一个数组)DefaultType
只使用给定函数的返回类型,如果不是函数,则使用给定类型本身
FieldType
是直接使用ValueType
的类型(如果可以应用,即如果它不是never
),或者执行两个附加检查:
- 字段可以使用
元素(也可能是默认值)显式声明其类型。如果 在这种情况下,我们在此元素上调用type
。这是 拆分ValueType
和ValueType
的原因,因为type不能 可以直接递归FieldType
- 字段本身可以是架构对象。在这种情况下,我们只处理
它本身就是这样,并调用下面定义的
docType
docType
,它只是将T
的每个属性映射到相应的FieldType
s
最后,在实际代码中,我们将模式存储到常量变量中;基于此变量类型的
docType
将是文档类型。为什么要使用String