Skip to content

Commit

Permalink
Fixed type-level enum overwrite of arrays (fixes drizzle-team#1110), …
Browse files Browse the repository at this point in the history
…added related type tests, removed unnecessary or repeating checks
  • Loading branch information
Sukairo-02 committed Apr 27, 2024
1 parent e0aaeb2 commit 3461c4e
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 46 deletions.
86 changes: 40 additions & 46 deletions drizzle-valibot/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,12 @@ type MaybeOptional<
type GetValibotType<TColumn extends Column> = TColumn['_']['dataType'] extends infer TDataType
? TDataType extends 'custom' ? AnySchema
: TDataType extends 'json' ? Json
: TDataType extends 'array'
? TColumn['_']['baseColumn'] extends Column ? ArraySchema<GetValibotType<TColumn['_']['baseColumn']>>
: never
: TColumn extends { enumValues: [string, ...string[]] }
? Equal<TColumn['enumValues'], [string, ...string[]]> extends true ? StringSchema
: PicklistSchema<TColumn['enumValues']>
: TDataType extends 'array'
? TColumn['_']['baseColumn'] extends Column ? ArraySchema<GetValibotType<TColumn['_']['baseColumn']>> : never
: TDataType extends 'bigint' ? BigintSchema
: TDataType extends 'number' ? NumberSchema
: TDataType extends 'string' ? StringSchema
Expand Down Expand Up @@ -156,8 +157,8 @@ export function createInsertSchema<
& string}' does not exist in table '${TTable['_']['name']}'`
>;
},
//
): ObjectSchema<
): // @ts-ignore - following error does not break types during usage in any way
ObjectSchema<
BuildInsertSchema<
TTable,
Equal<TRefine, Refine<TTable, 'insert'>> extends true ? {} : TRefine
Expand Down Expand Up @@ -271,8 +272,7 @@ function isWithEnum(
column: AnyColumn,
): column is typeof column & { enumValues: [string, ...string[]] } {
return (
'enumValues' in column
&& Array.isArray(column.enumValues)
Array.isArray(column.enumValues)
&& column.enumValues.length > 0
);
}
Expand All @@ -281,47 +281,41 @@ function mapColumnToSchema(column: Column): BaseSchema<any, any> {
let type: BaseSchema<any, any> | undefined;

if (isWithEnum(column)) {
type = column.enumValues?.length
? picklist(column.enumValues)
: string();
}

if (!type) {
if (column.dataType === 'custom') {
type = any();
} else if (column.dataType === 'json') {
type = jsonSchema;
} else if (column.dataType === 'array') {
type = array(
mapColumnToSchema((column as PgArray<any, any>).baseColumn),
);
} else if (column.dataType === 'number') {
type = number();
} else if (column.dataType === 'bigint') {
type = bigint();
} else if (column.dataType === 'boolean') {
type = boolean();
} else if (column.dataType === 'date') {
type = date();
} else if (column.dataType === 'string') {
let sType = string();

if (
(is(column, PgChar)
|| is(column, PgVarchar)
|| is(column, MySqlVarChar)
|| is(column, MySqlVarBinary)
|| is(column, MySqlChar)
|| is(column, SQLiteText))
&& typeof column.length === 'number'
) {
sType = string([maxLength(column.length)]);
}

type = sType;
} else if (is(column, PgUUID)) {
type = string([uuid()]);
type = picklist(column.enumValues);
} else if (column.dataType === 'custom') {
type = any();
} else if (column.dataType === 'json') {
type = jsonSchema;
} else if (column.dataType === 'array') {
type = array(
mapColumnToSchema((column as PgArray<any, any>).baseColumn),
);
} else if (column.dataType === 'number') {
type = number();
} else if (column.dataType === 'bigint') {
type = bigint();
} else if (column.dataType === 'boolean') {
type = boolean();
} else if (column.dataType === 'date') {
type = date();
} else if (column.dataType === 'string') {
let sType = string();

if (
(is(column, PgChar)
|| is(column, PgVarchar)
|| is(column, MySqlVarChar)
|| is(column, MySqlVarBinary)
|| is(column, MySqlChar)
|| is(column, SQLiteText))
&& typeof column.length === 'number'
) {
sType = string([maxLength(column.length)]);
}

type = sType;
} else if (is(column, PgUUID)) {
type = string([uuid()]);
}

if (!type) {
Expand Down
181 changes: 181 additions & 0 deletions drizzle-valibot/type-tests/mysql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import {
bigint,
binary,
boolean,
char,
customType,
date,
datetime,
decimal,
double,
float,
int,
json,
longtext,
mediumint,
mediumtext,
mysqlEnum,
mysqlTable,
real,
serial,
smallint,
text,
time,
timestamp,
tinyint,
tinytext,
varbinary,
varchar,
year,
} from 'drizzle-orm/mysql-core';
import type { Output } from 'valibot';
import { createInsertSchema, createSelectSchema } from '../src';
import { type Equal, Expect } from './utils';

const customInt = customType<{ data: number }>({
dataType() {
return 'int';
},
});

const testTable = mysqlTable('test', {
bigint: bigint('bigint', { mode: 'bigint' }).notNull(),
bigintNumber: bigint('bigintNumber', { mode: 'number' }).notNull(),
binary: binary('binary').notNull(),
boolean: boolean('boolean').notNull(),
char: char('char', { length: 4 }).notNull(),
charEnum: char('char', { enum: ['a', 'b', 'c'] }).notNull(),
customInt: customInt('customInt').notNull(),
date: date('date').notNull(),
dateString: date('dateString', { mode: 'string' }).notNull(),
datetime: datetime('datetime').notNull(),
datetimeString: datetime('datetimeString', { mode: 'string' }).notNull(),
decimal: decimal('decimal').notNull(),
double: double('double').notNull(),
enum: mysqlEnum('enum', ['a', 'b', 'c']).notNull(),
float: float('float').notNull(),
int: int('int').notNull(),
json: json('json').notNull(),
mediumint: mediumint('mediumint').notNull(),
real: real('real').notNull(),
serial: serial('serial').notNull(),
smallint: smallint('smallint').notNull(),
text: text('text').notNull(),
textEnum: text('textEnum', { enum: ['a', 'b', 'c'] }).notNull(),
tinytext: tinytext('tinytext').notNull(),
tinytextEnum: tinytext('tinytextEnum', { enum: ['a', 'b', 'c'] }).notNull(),
mediumtext: mediumtext('mediumtext').notNull(),
mediumtextEnum: mediumtext('mediumtextEnum', {
enum: ['a', 'b', 'c'],
}).notNull(),
longtext: longtext('longtext').notNull(),
longtextEnum: longtext('longtextEnum', { enum: ['a', 'b', 'c'] }).notNull(),
time: time('time').notNull(),
timestamp: timestamp('timestamp').notNull(),
timestampString: timestamp('timestampString', { mode: 'string' }).notNull(),
tinyint: tinyint('tinyint').notNull(),
varbinary: varbinary('varbinary', { length: 200 }).notNull(),
varchar: varchar('varchar', { length: 200 }).notNull(),
varcharEnum: varchar('varcharEnum', {
length: 1,
enum: ['a', 'b', 'c'],
}).notNull(),
year: year('year').notNull(),
autoIncrement: int('autoIncrement').notNull().autoincrement(),
});

const insertSchema = createInsertSchema(testTable);
const selectSchema = createSelectSchema(testTable);

type InsertType = Output<typeof insertSchema>;
type SelectType = Output<typeof selectSchema>;

Expect<
Equal<InsertType, {
bigint: bigint;
boolean: boolean;
json: string | number | boolean | any[] | {
[x: string]: any;
} | null;
date: Date;
bigintNumber: number;
binary: string;
char: string;
charEnum: 'a' | 'b' | 'c';
int: number;
customInt: any;
dateString: string;
datetime: Date;
datetimeString: string;
decimal: string;
double: number;
enum: 'a' | 'b' | 'c';
float: number;
mediumint: number;
real: number;
smallint: number;
text: string;
tinytext: string;
mediumtext: string;
longtext: string;
textEnum: 'a' | 'b' | 'c';
tinytextEnum: 'a' | 'b' | 'c';
mediumtextEnum: 'a' | 'b' | 'c';
longtextEnum: 'a' | 'b' | 'c';
time: string;
timestamp: Date;
timestampString: string;
tinyint: number;
varbinary: string;
varchar: string;
varcharEnum: 'a' | 'b' | 'c';
year: number;
serial?: number;
autoIncrement?: number;
}>
>;

Expect<
Equal<SelectType, {
bigint: bigint;
boolean: boolean;
enum: 'a' | 'b' | 'c';
json: string | number | boolean | any[] | {
[x: string]: any;
} | null;
date: Date;
bigintNumber: number;
binary: string;
char: string;
charEnum: 'a' | 'b' | 'c';
int: number;
customInt: any;
dateString: string;
datetime: Date;
datetimeString: string;
decimal: string;
double: number;
float: number;
mediumint: number;
real: number;
serial: number;
smallint: number;
text: string;
tinytext: string;
mediumtext: string;
longtext: string;
textEnum: 'a' | 'b' | 'c';
tinytextEnum: 'a' | 'b' | 'c';
mediumtextEnum: 'a' | 'b' | 'c';
longtextEnum: 'a' | 'b' | 'c';
time: string;
timestamp: Date;
timestampString: string;
tinyint: number;
varbinary: string;
varchar: string;
varcharEnum: 'a' | 'b' | 'c';
year: number;
autoIncrement: number;
}>
>;
66 changes: 66 additions & 0 deletions drizzle-valibot/type-tests/pg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { char, date, integer, pgEnum, pgTable, serial, text, timestamp, varchar } from 'drizzle-orm/pg-core';
import type { Output } from 'valibot';
import { createInsertSchema, createSelectSchema } from '../src';
import { type Equal, Expect } from './utils';

export const roleEnum = pgEnum('role', ['admin', 'user']);

const testTable = pgTable('users', {
intArr: integer('int_arr').array(),
strArr: text('str_arr').array(),
id: serial('id').primaryKey(),
name: text('name'),
email: text('email').notNull(),
birthdayString: date('birthday_string').notNull(),
birthdayDate: date('birthday_date', { mode: 'date' }).notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
role: roleEnum('role').notNull(),
roleText: text('role1', { enum: ['admin', 'user'] }).notNull(),
roleText2: text('role2', { enum: ['admin', 'user'] })
.notNull()
.default('user'),
profession: varchar('profession', { length: 20 }).notNull(),
initials: char('initials', { length: 2 }).notNull(),
});

const insertSchema = createInsertSchema(testTable);
const selectSchema = createSelectSchema(testTable);

type InsertType = Output<typeof insertSchema>;
type SelectType = Output<typeof selectSchema>;

Expect<
Equal<InsertType, {
role: 'admin' | 'user';
email: string;
birthdayString: string;
birthdayDate: Date;
roleText: 'admin' | 'user';
profession: string;
initials: string;
intArr?: number[] | null;
strArr?: string[] | null;
id?: number;
name?: string | null;
createdAt?: Date;
roleText2?: 'admin' | 'user';
}>
>;

Expect<
Equal<SelectType, {
role: 'admin' | 'user';
intArr: number[] | null;
strArr: string[] | null;
id: number;
name: string | null;
email: string;
birthdayString: string;
birthdayDate: Date;
createdAt: Date;
roleText: 'admin' | 'user';
roleText2: 'admin' | 'user';
profession: string;
initials: string;
}>
>;

0 comments on commit 3461c4e

Please sign in to comment.