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

情境: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: 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
    FieldType
    的原因,因为type不能 可以直接递归

  • 字段本身可以是架构对象。在这种情况下,我们只处理 它本身就是这样,并调用下面定义的
    docType

然后,它用于泛型类型
docType
,它只是将
T
的每个属性映射到相应的
FieldType
s


最后,在实际代码中,我们将模式存储到常量变量中;基于此变量类型的
docType
将是文档类型。

为什么要使用
String