Typescript 将数组映射到具有正确类型的对象(仅类型方式) 介绍

Typescript 将数组映射到具有正确类型的对象(仅类型方式) 介绍,typescript,typescript-typings,Typescript,Typescript Typings,在我们的项目中,我们有属性支持,其中每个属性都是类。该属性包含有关类型、可选性和名称的信息。我不想为每个实体定义接口,而是想将其自动化。我们有大约500个属性和100多个实体。实体是属性的收集器 例子 接口属性类{ 只读属性名称:字符串; 只读必需?:布尔值; 只读值类型:StringConstructor | NumberConstructor | BooleanConstructor; } 类属性扩展属性{ 静态只读attrName=“test”; 静态只读必需=真; 静态只读valueTy

在我们的项目中,我们有属性支持,其中每个属性都是类。该属性包含有关类型、可选性和名称的信息。我不想为每个实体定义接口,而是想将其自动化。我们有大约500个属性和100多个实体。实体是属性的收集器

例子
接口属性类{
只读属性名称:字符串;
只读必需?:布尔值;
只读值类型:StringConstructor | NumberConstructor | BooleanConstructor;
}
类属性扩展属性{
静态只读attrName=“test”;
静态只读必需=真;
静态只读valueType=String
}
类Attr2Test扩展属性{
静态只读属性attrName=“test2”;
静态只读valueType=Number
}
接口实体{
测试:字符串//属性测试
test2?:编号//属性测试
}
上课{
静态属性=[AttrTest,Attr2Test]
}
在这里您可以注意到,我有
valueType
,它保存了实类型。我也知道它的名字,如果它是可选的。(如果
必需的
存在并设置为true,则为必需)

概念和我的无效解决方案 我的想法是迭代
属性
数组,将值映射到名称并使其可选

  • 键入以筛选可选属性
  • export type ValueOf=T[keyof T];
    
    键入FilterOptionalAttribute

    我有其他但有效的解决方案

    // Declare constructor type
    type Constructor<T> = new (...args: any[]) => T;
    
    // Declare support attribute types
    type SupportTypes = [String, Number, Boolean];
    
    // Attribyte class
    class AttributeClass<K extends string, T extends SupportTypes[number], R extends boolean = false> {
      constructor(
        readonly attrName: K,
        readonly valueType: Constructor<T>,
        readonly required?: R,
      ) {
      }
    }
    
    
    // Declare test attributes
    const AttrTest = new AttributeClass('test', String, true);
    const Attr2Test = new AttributeClass('test2', Number);
    
    const attributes = [AttrTest, Attr2Test];
    
    // Unwrap instance of AttributeClass, to object
    type UnwrapAttribute<T> = T extends AttributeClass<infer K, infer T, infer R> ? (
      R extends true ? {
        [key in K]: T;
      } : {
        [key in K]?: T;
      }
    ) : never;
    
    // Transform union to intersection
    // Example: UnionToIntersection<{a: string} | {b: number}> => {a: string, b: number}
    type UnionToIntersection<U> = ((U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never);
    
    // Transform tuple to intersection
    // Example: TupleToIntersection<[{a: string}, {b: number}]> => {a: string, b: number}
    type TupleToIntersection<U extends Array<any>> = UnionToIntersection<U[number]>;
    
    // Map array of attributes
    type MapAttributes<ArrT extends Array<AttributeClass<any, any, any>>> = TupleToIntersection<{
      [I in keyof ArrT]: UnwrapAttribute<ArrT[I]>;
    }>;
    
    // Result
    const mapped: MapAttributes<typeof attributes> = {
      test: '123',
      test2: 123,
    };
    
    //声明构造函数类型
    类型构造函数=new(…参数:any[])=>T;
    //声明支持属性类型
    类型SupportTypes=[String,Number,Boolean];
    //属性类
    类属性类{
    建造师(
    只读属性名:K,
    只读valueType:构造函数,
    只读必需?:R,
    ) {
    }
    }
    //声明测试属性
    const AttrTest=new AttributeClass('test',String,true);
    常量Attr2Test=新属性类('test2',编号);
    常量属性=[AttrTest,Attr2Test];
    //将AttributeClass的实例展开到对象
    类型UnwapAttribute=T扩展属性类?(
    R是真的吗{
    [输入K]:T;
    } : {
    [输入K]?:T;
    }
    ):从不;
    //将并集变换为交集
    //示例:UnionToIntersection=>{a:string,b:number}
    输入UnionToIntersection=((U扩展任意?(k:U)=>void:never)扩展((k:inferi)=>void)?I:never);
    //将元组转换为交集
    //示例:tuplePointerSection=>{a:string,b:number}
    
    键入tuplePointerSection我有其他但有效的解决方案

    // Declare constructor type
    type Constructor<T> = new (...args: any[]) => T;
    
    // Declare support attribute types
    type SupportTypes = [String, Number, Boolean];
    
    // Attribyte class
    class AttributeClass<K extends string, T extends SupportTypes[number], R extends boolean = false> {
      constructor(
        readonly attrName: K,
        readonly valueType: Constructor<T>,
        readonly required?: R,
      ) {
      }
    }
    
    
    // Declare test attributes
    const AttrTest = new AttributeClass('test', String, true);
    const Attr2Test = new AttributeClass('test2', Number);
    
    const attributes = [AttrTest, Attr2Test];
    
    // Unwrap instance of AttributeClass, to object
    type UnwrapAttribute<T> = T extends AttributeClass<infer K, infer T, infer R> ? (
      R extends true ? {
        [key in K]: T;
      } : {
        [key in K]?: T;
      }
    ) : never;
    
    // Transform union to intersection
    // Example: UnionToIntersection<{a: string} | {b: number}> => {a: string, b: number}
    type UnionToIntersection<U> = ((U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never);
    
    // Transform tuple to intersection
    // Example: TupleToIntersection<[{a: string}, {b: number}]> => {a: string, b: number}
    type TupleToIntersection<U extends Array<any>> = UnionToIntersection<U[number]>;
    
    // Map array of attributes
    type MapAttributes<ArrT extends Array<AttributeClass<any, any, any>>> = TupleToIntersection<{
      [I in keyof ArrT]: UnwrapAttribute<ArrT[I]>;
    }>;
    
    // Result
    const mapped: MapAttributes<typeof attributes> = {
      test: '123',
      test2: 123,
    };
    
    //声明构造函数类型
    类型构造函数=new(…参数:any[])=>T;
    //声明支持属性类型
    类型SupportTypes=[String,Number,Boolean];
    //属性类
    类属性类{
    建造师(
    只读属性名:K,
    只读valueType:构造函数,
    只读必需?:R,
    ) {
    }
    }
    //声明测试属性
    const AttrTest=new AttributeClass('test',String,true);
    常量Attr2Test=新属性类('test2',编号);
    常量属性=[AttrTest,Attr2Test];
    //将AttributeClass的实例展开到对象
    类型UnwapAttribute=T扩展属性类?(
    R是真的吗{
    [输入K]:T;
    } : {
    [输入K]?:T;
    }
    ):从不;
    //将并集变换为交集
    //示例:UnionToIntersection=>{a:string,b:number}
    输入UnionToIntersection=((U扩展任意?(k:U)=>void:never)扩展((k:inferi)=>void)?I:never);
    //将元组转换为交集
    //示例:tuplePointerSection=>{a:string,b:number}
    
    键入tuplePointerSection我将回答这个问题的简化版本,它忽略了特定的类定义以及具有静态属性和实例的构造函数之间的差异。您可以在完整版本中使用下面介绍的常规技术,并进行适当的转换

    给定以下接口

    interface AttributeInterface {
      attrName: string;
      required?: boolean;
      valueType: StringConstructor | NumberConstructor | BooleanConstructor;
    }
    
    我将呈现一个
    数据类型
    ,它将
    T
    ,一个
    属性接口
    s的联合,转换为它所表示的实体。请注意,如果您有一个数组类型
    Arr
    [Att1,Att2]
    ,您可以通过它的
    number
    索引签名将其转换为一个联合:
    Arr[number]
    Att1 | Att2

    无论如何,这是:

    type DataType<T extends AttributeInterface> = (
      { [K in Extract<T, { required: true }>["attrName"]]:
        ReturnType<Extract<T, { attrName: K }>["valueType"]> } &
      { [K in Exclude<T, { required: true }>["attrName"]]?:
        ReturnType<Extract<T, { attrName: K }>["valueType"]> }
    ) extends infer O ? { [K in keyof O]: O[K] } : never;
    
    这意味着我们可以通过使用实用程序类型轻松地获得基元类型

    唯一需要解释的是
    。。。你猜怎么着?{[K in keyof]:O[K]}:从不
    。这是一个技巧,可以将交集类型(如
    {foo:string}&{bar?:number}
    )转换为单个对象类型(如
    {foo:string;bar?:number}


    同样,将其转换为采用数组类型的形式也很简单:

    type DataTypeFromArray<T extends AttributeInterface[]> = DataType<T[number]>;
    
    type AlsoEntity = DataTypeFromArray<[AttrTest, Attr2Test]>;
    /* type AlsoEntity = {
        test: string;
        test2?: number | undefined;
    } */
    
    type DataTypeFromArray=DataType;
    类型AlsoEntity=DataTypeFromArray;
    /*类型AlsoEntity={
    测试:字符串;
    test2?:编号|未定义;
    } */
    
    这将有助于为示例代码中的类构建解决方案


    好吧,希望这会有帮助;祝你好运


    我将回答这个问题的简化版本,它忽略了特定的类定义以及具有静态属性和实例的构造函数之间的差异。您可以在完整版本中使用下面介绍的常规技术,并进行适当的转换

    给定以下接口

    interface AttributeInterface {
      attrName: string;
      required?: boolean;
      valueType: StringConstructor | NumberConstructor | BooleanConstructor;
    }
    
    我将呈现一个
    数据类型
    ,它将
    T
    ,一个
    属性接口
    s的联合,转换为它所表示的实体。请注意,如果您有一个数组类型
    Arr
    [Att1,Att2]
    ,您可以通过它的
    number
    索引签名将其转换为一个联合:
    Arr[number]
    Att1 | Att2

    无论如何,这是:

    type DataType<T extends AttributeInterface> = (
      { [K in Extract<T, { required: true }>["attrName"]]:
        ReturnType<Extract<T, { attrName: K }>["valueType"]> } &
      { [K in Exclude<T, { required: true }>["attrName"]]?:
        ReturnType<Extract<T, { attrName: K }>["valueType"]> }
    ) extends infer O ? { [K in keyof O]: O[K] } : never;
    
    这意味着我们可以通过使用实用程序类型轻松地获得基元类型

    唯一需要解释的是
    。。。你猜怎么着?{[K in keyof]:O[K]}:从不
    。这是一个采用交集类型(如
    {f)的技巧