Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何推断TypeScript中的可选属性?_Typescript_Type Inference - Fatal编程技术网

如何推断TypeScript中的可选属性?

如何推断TypeScript中的可选属性?,typescript,type-inference,Typescript,Type Inference,对于数据库架构,我有以下类型定义: 类型架构={ [K in keyof T]:模式类型 } 接口模式类型{ 可选:布尔值 验证(t:t):布尔值 } 下面是一个实现它的对象: const userSchema={ 姓名:{ 可选:true, 验证(名称:字符串){ 返回name.length>0&&name.length0&&name.lengthoptional设置为true //else->optional设置为false。 //我希望TypeScript在推断类型时可以逆转这一点: /

对于数据库架构,我有以下类型定义:

类型架构={
[K in keyof T]:模式类型
}
接口模式类型{
可选:布尔值
验证(t:t):布尔值
}
下面是一个实现它的对象:

const userSchema={
姓名:{
可选:true,
验证(名称:字符串){
返回name.length>0&&name.length<30
}
}
}
我希望能够根据
userSchema
对象推断
Schema
中的泛型类型
T
。使用TypeScript的
推断
关键字,无需花费太多精力即可完成此操作:

type extractType=T扩展模式?X:null
//用户被推断为{name:string}
类型User=extractType
更好的是,我能够间接推断
用户
类型:

类模型{
构造函数(私有模式:模式){}
}
//userModel被推断为类型模型
const userModel=新模型(userSchema)
但是,我希望能够将
User
推断为
{name?:String}
,其中
name
是可选的,因为它的
optional
属性设置为true。有办法做到这一点吗

一些相关的打字脚本功能 下面是我在处理这个问题时遇到的一些潜在有用的TypeScript特性

  • TypeScript有,这类似于我试图实现的类型推断

  • 尽管上面的
    用户模式
    上的
    可选
    字段被推断为
    布尔值
    ,但是可以使用
    as const
    将其推断为布尔值文本

    //用户架构推断为
    //{name:{可选:true,验证(名称:字符串):boolean}
    //而不是
    //{名称:{可选:布尔,验证(名称:字符串):布尔}
    const userSchema={
    姓名:{
    可选:作为常量为true,
    验证(名称:字符串){
    返回name.length>0&&name.length<30
    }
    }
    }
    
    要求这样做并不理想,但我不确定这是可以避免的

  • 我尝试在上面的
    extractType
    实现中使用like,以更有限的方式定义
    Schema

    //下面的界面本质上是这样的:
    //如果T[K]是可选属性
    //然后->optional设置为true
    //else->optional设置为false。
    //我希望TypeScript在推断类型时可以逆转这一点:
    //如果optional设置为true
    //然后->T[K]是可选属性
    //else->T[K]是必需的属性
    类型架构={
    [K in keyof T]:未定义的扩展T[K]
    ?OptionalSchemaType//与SchemaType相同,但带有可选项:true
    :RequiredSchemaType//与SchemaType相同,但可选:false
    }
    const userModel=new Model(userSchema)//错误!
    
    不幸的是,TypeScript的类型推断不够聪明,无法推断出如此复杂的类型


  • 我不确定我想要实现的目标是否可能,但非常感谢任何帮助或替代方案。:)

    首先,您可能需要将
    true编写为const
    true编写为true
    或类似的内容,因为编译器会倾向于将布尔文本加宽为type
    boolean
    ,除非它们是在编译器要求更窄类型的上下文中声明的。避免显式强制转换的一种可能方法是使用一个helper函数,该函数返回其输入,它希望在这样一个较窄的类型上下文中返回该输入:

    const asSchema = <S extends Record<keyof S, { optional: B }>, B extends boolean>(
        s: S
    ) => s;
    
    const userSchema = asSchema({
        name: {
            optional: true,
            validate(name: string) {
                return name.length > 0 && name.length < 30
            }
        },
        age: {
            optional: false,
            validate(age: number) {
                return age >= 0 && age < 200;
            }
        }
    });
    
    如果你打算创建很多这样的东西,那么使用helper函数可能是值得的;或者您可以只编写
    可选:false as false
    。无论如何,继续:


    我认为您需要直接计算类型,而不是依赖类型推断。直接方法需要显式地写出类型的哪些部分转化为哪些部分。让我们想出一些实用程序类型:

    PartialPartial
    类型接受一个类型
    T
    及其一组键
    K
    ,并返回一个新类型,如
    T
    ,但
    K
    索引的所有属性都是可选的。所以,
    PartialPartial
    {a?:string,b:number}

    type PartialPartial<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K> extends
        infer O ? { [P in keyof O]: O[P] } : never;
    
    现在是
    ExtractType
    。给定一个类似于
    T
    的模式,首先映射其每个属性,并将第一个参数提取到
    validate()
    方法。然后,获取整个结果,并将其
    optional
    属性为
    true
    的所有属性设置为可选:

    type ExtractType<T extends { [K in keyof T]: SchemaType<any> }> = PartialPartial<{
        [K in keyof T]: Parameters<T[K]['validate']>[0]
    }, KeysMatching<T, { optional: true }>>
    
    看起来不错


    如果我们放弃类型推断,这意味着你不能轻易地用
    Schema
    构建
    Model
    (至少不能用标准的
    class
    语句)。解决这个问题的一种方法是让模式类型
    S
    中的
    Model
    是泛型的,并在需要使用
    T
    的任何地方使用
    ExtractType
    。您甚至可以将
    T
    放在类型定义中,这样您就有了
    Model
    ,其中
    S
    是模式类型,
    T
    是提取的类型:

    class Model<S extends Record<keyof S, SchemaType<any>>, T extends ExtractType<S> = ExtractType<S>> {
        constructor(private schema: S) { }
    }
    
    const userModel = new Model(userSchema);
    // S is typeof userSchema
    // T is {name?: string, age: number}
    
    并测试它:

    const myUserModel = new MyModel(userSchema);
    /* const myUserModel: MyModel<{
        name?: string | undefined;
        age: number;
    }> */
    
    constmyusermodel=newmymodel(userSchema);
    /*常量myUserModel:MyModel*/
    
    在这里您可以看到
    newmymodel(userSchema)
    生成一个
    MyModel
    。这很好,但您会发现自己必须为类的静态/实例端编写冗余接口声明,然后为其分配其他类构造函数。重复可能不值得;这取决于你


    好吧,我知道那是很多东西。希望对你有帮助。祝你好运


    接口模式{[K in keyof T]:模式类型}
    不是有效的TS;大概你的意思是
    typeschema=…
    。修复了!谢谢你指出这一点。非常感谢!这正是我要找的。我不确定这是否是你所尝试的
    type UserThing = ExtractType<typeof userSchema>;
    /* type UserThing = {
        name?: string | undefined;
        age: number;
    } */
    
    class Model<S extends Record<keyof S, SchemaType<any>>, T extends ExtractType<S> = ExtractType<S>> {
        constructor(private schema: S) { }
    }
    
    const userModel = new Model(userSchema);
    // S is typeof userSchema
    // T is {name?: string, age: number}
    
    interface MyModelConstructor {
        new <S extends Record<keyof S, SchemaType<any>>>(schema: S): MyModel<ExtractType<S>>;
    }
    interface MyModel<T> {
        something: T;
    }
    const MyModel = Model as any as MyModelConstructor;
    
    const myUserModel = new MyModel(userSchema);
    /* const myUserModel: MyModel<{
        name?: string | undefined;
        age: number;
    }> */