ساخت نوع TypeScript از آرایهای از اشیاء
من ستونهای طرحبندی زیر را برای یک جدول پایگاه داده (مورد استفاده برای Watermelon DB) دارم.
const columns = [
{ name: "created_at", type: "number", isOptional: true },
{ name: "created_by", type: "string" },
{ name: "is_corrupt", type: "boolean", isOptional: true },
];
من میخواهم یک ژنریک ایجاد کنم که نوع زیر را از مثال بالا بسازد، چگونه میتوانم این کار را انجام دهم؟
type ExpectedInferredTypeFromColumns = {
created_at: number | null;
created_by: string;
is_corrupt: boolean | null;
};
تلاش من:
type InferTypeFromColumns<T extends ReadonlyArray<Column>> = {
[K in T extends ReadonlyArray<infer U>
? U extends { name: string }
? U["name"]
: never
: never]: T extends ReadonlyArray<infer U>
? U extends { type: "number"; isOptional: true }
? number | null
: U extends { type: "number" }
? number
: U extends { type: "string"; isOptional: true }
? string | null
: U extends { type: "string" }
? string
: U extends { type: "boolean"; isOptional: true }
? boolean | null
: U extends { type: "boolean" }
? boolean
: never
: never;
};
type MyInferredType = InferTypeFromColumns<typeof columns>;
// Produces: =>
// type MyInferredType = {
// created_at: string | number | boolean | null;
// created_by: string | number | boolean | null;
// is_corrupt: string | number | boolean | null;
// }
همانطور که میبینید تلاش من کاملاً با ExpectedInferredTypeFromColumns من مطابقت ندارد
پاسخ شماره ۱
بفرما: زمین بازی
const columns = [
{ name: "created_at", type: "number", isOptional: true },
{ name: "created_by", type: "string" },
{ name: "is_corrupt", type: "boolean", isOptional: true },
] as const; // define as const so `columns[number]` gives precise type inference
type Column = {
name: string;
type: "number" | "string" | "boolean"
isOptional?: boolean
}
type TypeMapper = {
boolean: boolean;
string: string;
number: number;
}
// You need to create a union depending if `isOptional` is defined or not
type InferTypeFromColumns<T extends ReadonlyArray<Column>> = {
[K in T[number] as K['name']]: TypeMapper[K['type']] | (K['isOptional'] extends true ? null : never)
}
type Test = InferTypeFromColumns<typeof columns>
/*
type Test = {
created_at: number | null;
created_by: string;
is_corrupt: boolean | null;
}
*/
اگر نیاز دارید کلیدهای اختیاری را داشته باشید، باید یک اشتراک از کلیدهای اختیاری و الزامی به این صورت انجام دهید:
type RequiredInferTypeFromColumns<T extends ReadonlyArray<Column>> = {
[K in T[number] as K['isOptional'] extends true ? never : K['name']]: TypeMapper[K['type']]
}
type OptionalInferTypeFromColumns<T extends ReadonlyArray<Column>> = {
[K in T[number] as K['isOptional'] extends true ? K['name'] : never]?: TypeMapper[K['type']] | null
}
type Intersection<A, B> = A & B extends infer U
? { [P in keyof U]: U[P] }
: never;
type Test = Intersection<RequiredInferTypeFromColumns<typeof columns>, OptionalInferTypeFromColumns<typeof columns>>
/*
type Test = {
created_by: string;
created_at?: number | null | undefined;
is_corrupt?: boolean | null | undefined;
}
*/
این پاسخ از این راه حل الهام گرفته شده است: اعمال شرطی تغییرگر ? در نوع نگاشت شده برای هر ویژگی
پاسخ شماره ۲
خب، بنابراین با اساساً کپی کردن این جواب توانستم یک راهحل عملی پیدا کنم
const columns = [
{ name: "created_at", type: "number", isOptional: true },
{ name: "created_by", type: "string", isOptional: false },
{ name: "is_corrupt", type: "boolean", isOptional: true },
] as const;
type InferTypeFromColumns<P extends readonly unknown[]> = {
[K in IndexKeys<P> as Name<P[K]>]: Type<P[K]>;
};
type IndexKeys<A extends readonly unknown[]> = Exclude<keyof A, keyof []>;
type Name<O> = O extends { name: infer N }
? N extends string
? N
: never
: never;
type Type<T> = T extends { type: "number"; isOptional: true }
? number | null
: T extends { type: "number" }
? number
: T extends { type: "string"; isOptional: true }
? string | null
: T extends { type: "string" }
? string
: T extends { type: "boolean"; isOptional: true }
? boolean | null
: T extends { type: "boolean" }
? boolean
: never;
type MyInferredType = InferTypeFromColumns<typeof columns>;
