I have the following schema columns for a database table (used for Watermelon DB).
const columns = [
{ name: "created_at", type: "number", isOptional: true },
{ name: "created_by", type: "string" },
{ name: "is_corrupt", type: "boolean", isOptional: true },
];
I would like to create a generic which will create the following type from the above example, how can I do that?
type ExpectedInferredTypeFromColumns = {
created_at: number | null;
created_by: string;
is_corrupt: boolean | null;
};
My attempt:
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;
// }
As you can see my attempt doesn't quite meet my ExpectedInferredTypeFromColumns
CodePudding user response:
There you go: playground
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;
}
*/
CodePudding user response:
Okay so by basically copying this answer I was able to get a working solution
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>;