简体   繁体   English

如何从复杂对象的某些属性创建自定义类型?

[英]How to create a custom type from some properties of a complex object?

I have the following type definitions as helpers:我有以下类型定义作为助手:

type MapSchemaTypes = {
    string: string;
    number: number;
    integer: number;
    float: number;
    boolean: boolean;
};

type MapSchema<T extends Record<string, keyof MapSchemaTypes>> = {
    -readonly [K in keyof T]: MapSchemaTypes[T[K]]
}

With that, I can generate a type from a simple object.有了这个,我可以从一个简单的对象生成一个类型。 For example:例如:

const TestModel1 = {
    id: "number",
    lastname: "string",
    firstname: "string",
    valid: "boolean"
} as const;
type TestRecord1 = MapSchema<typeof TestModel1>;

Result is correct:结果正确:

type TestRecord1 = {
    id: number;
    lastname: string;
    firstname: string;
    valid: boolean;
}

But now, I would like to generate the exact same type but from a more complex object:但是现在,我想从更复杂的对象生成完全相同的类型:

const TestModel2 = {
    id: {
        type: "number",
        mapping: "_id",
        defaultValue: 0
    },
    lastname: {
        type: "string",
        mapping: "_lastname",
        defaultValue: ""
    },
    firstname: {
        type: "string",
        mapping: "_firstname",
        defaultValue: ""
    },
    valid: {
        type: "boolean",
        mapping: "_valid",
        defaultValue: false
    }
} as const;
type TestRecord2 = MapSchema<typeof TestModel2>;

Errors:错误:

Type '{ readonly id: { readonly type: "number"; readonly mapping: "_id"; readonly defaultValue: 0; }; readonly lastname: { readonly type: "string"; readonly mapping: "_lastname"; readonly defaultValue: ""; }; readonly firstname: { ...; }; readonly valid: { ...; }; }' does not satisfy the constraint 'Record<string, "string" | "number" | "boolean" | "integer" | "float">'.
Property 'id' is incompatible with index signature.
    Type '{ readonly type: "number"; readonly mapping: "_id"; readonly defaultValue: 0; }' is not assignable to type '"string" | "number" | "boolean" | "integer" | "float"'.
    Type '{ readonly type: "number"; readonly mapping: "_id"; readonly defaultValue: 0; }' is not assignable to type '"float"'.ts(2344)

It obviously generates the wrong type:它显然生成了错误的类型:

type TestRecord2 = {
    id: unknown;
    lastname: unknown;
    firstname: unknown;
    valid: unknown;
}

And I cannot figure how to do it correctly.我无法弄清楚如何正确地做到这一点。 I know that I have to change the MapSchema type but everything I tried failed so far.我知道我必须更改 MapSchema 类型,但到目前为止我尝试的一切都失败了。

For example I tried:例如我试过:

type MapSchema<T extends Record<string, keyof MapSchemaTypes>> = {
    -readonly [K in keyof T]: MapSchemaTypes[T[K]["type"]]
}

But it gives me the following error:但它给了我以下错误:

Type 'T[K]["type"]' cannot be used to index type 'MapSchemaTypes'.ts(2536)
Type '"type"' cannot be used to index type 'T[K]'.ts(2536)

Or again:或者再次:

type MapSchema<T extends Record<string, keyof MapSchemaTypes>> = {
    -readonly [K in keyof T]: MapSchemaTypes[T[K].type]]
}

Error:错误:

Cannot find name 'type'.ts(2304)

Is it even possible to achieve what I want to do?甚至有可能实现我想做的事情吗?

Update更新

Following @Titian Cernicova-Dragomir very helpful answer , here are my updated type helpers:遵循@Titian Cernicova-Dragomir非常有用的答案,这里是我更新的类型助手:

type MapSchemaTypes = {
    string: string;
    number: number;
    integer: number;
    float: number;
    boolean: boolean;
};
type MapSchemaDefaultValues = string | number | boolean | undefined | null;
type MapSchemaSimpleField   = keyof MapSchemaTypes;
type MapSchemaComplexField  = {
    type: MapSchemaSimpleField;
    mapping: string;
    defaultValue: MapSchemaDefaultValues
};
type MapSchemaField = MapSchemaSimpleField | MapSchemaComplexField;
type MapSchemaDefinition = Record<string, MapSchemaField>;
type MapSchema<T extends MapSchemaDefinition> = {
    -readonly [K in keyof T]: T[K] extends { type: infer TypeName } ? MapSchemaTypes[TypeName & MapSchemaSimpleField] : MapSchemaTypes[T[K] & MapSchemaSimpleField]
};

This lets me do the following:这让我可以执行以下操作:

const TestModel4 = {
    id: "number",
    lastname: {
        type: "string",
        mapping: "_lastname",
        defaultValue: ""
    },
    firstname: "string",
    valid: {
        type: "boolean",
        mapping: "_valid",
        defaultValue: false
    }
} as const;
type TestRecord4 = MapSchema<typeof TestModel4>;

And the resulting type is correct:结果类型是正确的:

type TestRecord4 = {
    id: number;
    lastname: string;
    firstname: string;
    valid: boolean;
}

The constraint you have on T is wrong if you want the properties of T to be an object with a type property, you should use Record<string, { type: keyof MapSchemaTypes }>如果您希望T的属性是具有类型属性的对象,则您对T的约束是错误的,您应该使用Record<string, { type: keyof MapSchemaTypes }>

type MapSchema<T extends Record<string, { type: keyof MapSchemaTypes }>> = {
    -readonly [K in keyof T]: MapSchemaTypes[T[K]["type"]]
}

Playground Link 游乐场链接

If you want it to work for both complex objects and just the type name, you can use a union in the constraint and a conditional type to discriminate between the two casses:如果您希望它既适用于复杂对象又适用于类型名称,则可以在约束中使用联合和条件类型来区分两种情况:

type MapSchema<T extends Record<string, { type: keyof MapSchemaTypes } | keyof MapSchemaTypes>> = {
    -readonly [K in keyof T]: T[K] extends { type: infer TypeName } ? MapSchemaTypes[TypeName &  keyof MapSchemaTypes] : MapSchemaTypes[T[K] & keyof MapSchemaTypes]
}

Playground Link 游乐场链接

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

相关问题 Typescript - 从 object 数组的公共属性创建自定义类型 - Typescript - Create custom type from common properties of an Array of object 如何创建原始类型和自定义类型的联合,原始类型的某些属性已更改并保持不变? - How to create union of original type and custom type with some properties of original changed and rest the same? 如何创建需要某些属性但可以容忍任何其他属性的类型? - How to create a type that requires some properties but tolerates any additional properties? 如何将一些属性从 object 复制到另一个 - How to copy some properties from an object to another 如何 object 的 select 属性不是某种类型的键? - How to select properties of object which are not keys of some type? 如何键入一个转换输入对象某些属性的函数 - How to type a function that transforms some properties of an input object 如何正确键入从对象中选取属性的自定义选择器函数 - How to properly type a custom selector function, which picks properties from an object 创建一个类型,它是对象属性的数组 - Create a type that is an array of an object properties 根据类型签名中具有确切属性的其他对象创建一个对象 - Create an object from other objects with exact properties in the type signature 如何打出这个复杂的object idea? - How to type this complex object idea?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM