typescript - 将数组映射到具有正确类型的对象(仅类型方式)
问题描述
介绍
在我们的项目中,我们有属性支持,其中每个属性都是类。该属性包含有关类型、可选性和名称的信息。我不想为每个实体定义一个接口,而是让它自动化。我们有大约 500 个属性和 100 多个实体。实体是属性的收集器。
例子
interface AttributeClass {
readonly attrName: string;
readonly required?: boolean;
readonly valueType: StringConstructor | NumberConstructor | BooleanConstructor;
}
class AttrTest extends Attribute {
static readonly attrName = "test";
static readonly required = true;
static readonly valueType = String
}
class Attr2Test extends Attribute {
static readonly attrName = "test2";
static readonly valueType = Number
}
interface Entity {
test: string // AttrTest
test2?: number // Attr2Test
}
class SomeClass {
static attributes = [AttrTest, Attr2Test]
}
在这里你可以注意到,我有valueType
哪个持有真正的类型。我也知道这个名字,如果它是可选的。(如果required
存在且设置为 true,则为必需)
概念和我不工作的解决方案
我的想法是遍历attributes
数组,将值映射到名称并使其可选。
- 键入以过滤可选属性
export type ValueOf<T> = T[keyof T];
type FilterOptionalAttribute<Attr extends AttributeClass> = ValueOf<Attr["required"]> extends false | undefined | null ? Attr : never
- 键入以过滤所需的属性
type FilterRequiredAttribute<Attr extends AttributeClass> = FilterOptionalAttribute<Attr> extends never ? Attr : never
- 要从 Type 转换为原始类型的类型
type ExtractPrimitiveType<A> =
A extends StringConstructor ? string :
A extends NumberConstructor ? number :
A extends BooleanConstructor ? boolean :
never
- 从类转换为键值对象的类型(必需 + 可选)
type AttributeDataType<Attr extends AttributeClass> = { [K in Attr["attrName"]]: ExtractPrimitiveType<Attr["valueType"]> }
type OptionalAttributeDataType<Attr extends AttributeClass> = { [K in Attr["attrName"]]?: ExtractPrimitiveType<Attr["valueType"]> }
- 把它粘在一起 + 一些东西来推断数组类型
type UnboxAttributes<AttrList> = AttrList extends Array<infer U> ? U : AttrList;
type DataType<AttributeList extends AttributeClass[]> = OptionalAttributeDataType<FilterOptionalAttribute<UnboxAttributes<AttributeList>>> & AttributeDataType<FilterRequiredAttribute<UnboxAttributes<AttributeList>>>
我对输出有什么期望
class SomeClass {
static attributes = [AttrTest, Attr2Test]
}
// notice double equals
const mapped: DataType<typeof SomeClass.attributes> == {
test: string
test2?: number
}
IDE 显示的内容
使用 IntelliJ IDEA Ultimate
// notice double equals
const mapped: DataType<typeof SomeClass.attributes> == {
test: string | number
test2: number | number
}
我已经花了 5 个小时来解决它。似乎我错过了一些重要的东西。我要感谢所有给我任何提示的人我做错了什么。
有两个问题:
- 一切都是必需的(test2 应该是可选的)
- 即使我在推断它们的类型也是混合的
解决方案
我有其他但有效的解决方案......
// 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,
};
推荐阅读
- c# - 使用 Autofac 将 SignalR IHubContext 注入服务层
- python-3.x - 如何在方法之后隔开 tkinter 排队的函数?
- reactjs - 部署到 Netlify 时 Gatsby 构建失败
- c# - 对于 c# 解决方案,如何修复程序集/文件依赖项运行时错误?
- javascript - Express 下载文件而不是将其用作 html
- php - 来自数组的PHP目录迭代器并返回每个目录中的最新文件
- c - 在 Linux 驱动程序中从 eMMC 磁盘读取原始 MMC 数据
- jquery - 避免特定调用者的 jQuery 事件
- php - 调用字符串上的成员函数 redirect() - Yii2 v2.0.36
- apache-spark - Pyspark Series2Series UDF,结构数组作为输入,结构作为输出