简体   繁体   English

推断类的 object 的类型

[英]Infer type of object of classes

Building a form config builder, want to infer types dynamically, here's the minimal reproduction code构建一个表单配置构建器,想要动态推断类型,这里是最小的复制代码

interface Meta {
    optional: boolean
}

class Base<Type = any> {
    readonly _type!: Type
    baseMeta: Meta

    constructor() {
        this.baseMeta = {
            optional: false,
        }
    }

    optional() {
        this.baseMeta.optional = true
        return this;
    }
}

class FieldText extends Base<string> {

}

class FieldNumber extends Base<number> {

}


class Field {
    static text() {
        return new FieldText();
    }

    static number() {
        return new FieldNumber();
    }
}

const fields = {
    fieldText: Field.text(),
    fieldNumber: Field.number(),
    fieldTextOptional: Field.text().optional(),
    fieldNumberOptional: Field.number().optional(),
}

Given code above I want to achieve one of the following types, doesn't matter which one给定上面的代码,我想实现以下类型之一,不管是哪一个

type ExpectedType = {
    fieldText: string;
    fieldNumber: number;
    fieldTextOptional: number | undefined;
    fieldNumberOptional: number | undefined;
}

Also fine也不错

type ExpectedTypeWithOptionalKeys = {
    fieldText: string;
    fieldNumber: number;
    fieldTextOptional?: number;
    fieldNumberOptional?: number;
}

I've gotten to infering types, but can't figure out why the optional property is not taken in consideration我已经开始推断类型,但无法弄清楚为什么不考虑可选属性

type ExtractFieldType<O, T> = O extends true ? T | undefined : T;

type SchemaType<T extends Record<string, Base>> = {
    [Property in keyof T]: ExtractFieldType<T[Property]['baseMeta']['optional'], T[Property]['_type']>;
};

type Schema = SchemaType<typeof fields>;

type Schema results in: type Schema导致:

type Schema = {
    fieldText: string;
    fieldNumber: number;
    fieldTextOptional: string;
    fieldNumberOptional: number;
}

this O extends true? T | undefined: T这个O extends true? T | undefined: T O extends true? T | undefined: T O extends true? T | undefined: T always returns T, doesn't take actual value of optional into consideration O extends true? T | undefined: T总是返回 T,不考虑 optional 的实际值

EDIT:编辑:

after a bit of fumbling around with @kelly 's answer for providing a value to the optional function here's the end result:在对@kelly为可选 function 提供值的答案进行了一番摸索之后,这是最终结果:

    optional<T extends boolean>(value?: T): Base<Type, T> {
        const optional = typeof value === 'boolean' ? value : true;
        this.baseMeta.optional = optional as Optional;
        return this as unknown as Base<Type, T>;
    }

first you can't have a default value as then Typescript get's angry with:首先你不能有一个默认值,然后 Typescript 会生气:

Type 'boolean' is not assignable to type 'T'.
  'boolean' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'boolean'.(2322)

So we simply make value optional and then the rest works pretty much as Kelly explained因此,我们只需将 value 设为optional ,然后 rest 就像 Kelly 解释的那样工作

except for return this as unknown as Base<Type, T>;除了return this as unknown as Base<Type, T>;

if you remove the as unknown part you get the following error:如果您删除as unknown部分,则会收到以下错误:

Conversion of type 'this' to type 'Base<Type, T>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
  Type 'Base<Type, Optional>' is not comparable to type 'Base<Type, T>'.
    Type 'Optional' is not comparable to type 'T'.
      'Optional' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'boolean'.(2352)

Here's the final working in Typescript Playground这是Typescript Playground中的最终工作

The type of optional in Meta is always boolean . Meta中的optional类型始终boolean You need to store the value in a generic to use later:您需要将值存储在泛型中以供以后使用:

interface Meta<Optional extends boolean> {
    optional: Optional;
}

class Base<Type = any, Optional extends boolean = false> {
    readonly _type!: Type
    baseMeta: Meta<Optional>

    constructor() {
        this.baseMeta = {
            optional: false as Optional,
        }
    }

    optional(): Base<Type, true> {
        this.baseMeta.optional = true as Optional; // needs unsafe assert, or alternatively //@ts-ignore
        return this as Base<Type, true>;
    }
}

Then when you change it with optional , trick TypeScript into believing you are returning Base<Type, true> instead.然后,当您使用optional更改它时,欺骗 TypeScript 相信您正在返回Base<Type, true>

You only need to change this.你只需要改变它。 Your original solution now works.您的原始解决方案现在有效。


Addressing the followup in the comments:解决评论中的后续问题:

optional<T extends boolean = true>(value: T = true): Base<Type, T> {
    this.baseMeta.optional = value as Optional;
    return this as Base<Type, T>;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM