DT
Drizzle Teamā€¢16mo ago
JT

MySqlInsertValue<> vs typeof Table type mismatch

I've got this line of code:
await db.insert(table).values(props);
await db.insert(table).values(props);
Where props is showing a TS problem:
Argument of type 'ModelMap[T]["select"]' is not assignable to parameter of type 'MySqlInsertValue<ModelMap[T]["model"]>'.
Type '{ id: string; createdAt: Date; updatedAt: Date; username: string; email: string; realName: string; password: string; passwordType: "bcrypt"; useAsDisplayName: "username" | "email" | "realName"; admin: boolean; developer: boolean; } | { ...; }' is not assignable to type 'MySqlInsertValue<ModelMap[T]["model"]>'.
Type '{ id: string; createdAt: Date; updatedAt: Date; username: string; email: string; realName: string; password: string; passwordType: "bcrypt"; useAsDisplayName: "username" | "email" | "realName"; admin: boolean; developer: boolean; }' is not assignable to type 'MySqlInsertValue<ModelMap[T]["model"]>'.
Argument of type 'ModelMap[T]["select"]' is not assignable to parameter of type 'MySqlInsertValue<ModelMap[T]["model"]>'.
Type '{ id: string; createdAt: Date; updatedAt: Date; username: string; email: string; realName: string; password: string; passwordType: "bcrypt"; useAsDisplayName: "username" | "email" | "realName"; admin: boolean; developer: boolean; } | { ...; }' is not assignable to type 'MySqlInsertValue<ModelMap[T]["model"]>'.
Type '{ id: string; createdAt: Date; updatedAt: Date; username: string; email: string; realName: string; password: string; passwordType: "bcrypt"; useAsDisplayName: "username" | "email" | "realName"; admin: boolean; developer: boolean; }' is not assignable to type 'MySqlInsertValue<ModelMap[T]["model"]>'.
The variables in there are typed like:
db: MySql2Database
table: ModelMap[T]['model']
props: ModelMap[T]['select']
db: MySql2Database
table: ModelMap[T]['model']
props: ModelMap[T]['select']
The ModelMap is an index of all the different types generated by various drizzle schemas I have, so that they are available for quick reference by just passing T. ModelMap[T]['model'] is equivalent to typeof SomeTable and ModelMap[T]['select'] is equivalent to InferModel<typeof SomeTable> What might be causing this problem?
16 Replies
JT
JTā€¢16mo ago
For completeness here's some extra info. a table:
import type { InferModel } from 'drizzle-orm/mysql-core';
import { boolean, mysqlEnum, mysqlTable, timestamp, uniqueIndex, varchar, text } from 'drizzle-orm/mysql-core';



export const UserTable = mysqlTable('users',
{
id: varchar('id', { length: 36 }).notNull().default('uuid-will-be-generated').primaryKey(),
createdAt: timestamp('createdAt').defaultNow().notNull(),
updatedAt: timestamp('updatedAt').defaultNow().notNull(),
username: varchar('username', { length: 60 }).notNull().default(''),
email: varchar('email', { length: 256 }).notNull().default(''),
realName: varchar('realName', { length: 60 }).notNull().default(''),
password: varchar('password', { length: 256 }).notNull().default('no-password-specified'),
passwordType: mysqlEnum('passwordType', ['bcrypt']).notNull().default('bcrypt'),
useAsDisplayName: mysqlEnum('useAsDisplayName', ['username','email','realName']).notNull().default('username'),
admin: boolean('admin').notNull().default(false),
developer: boolean('developer').notNull().default(false)
},
(table) => ({
usernameIndex: uniqueIndex('usernameIndex').on(table.username),
emailIndex: uniqueIndex('emailIndex').on(table.email)
})
);


export type UserModel = typeof UserTable;
export type UserSelect = InferModel<UserModel, 'select'>
export type UserInsert = InferModel<UserModel, 'insert'>
import type { InferModel } from 'drizzle-orm/mysql-core';
import { boolean, mysqlEnum, mysqlTable, timestamp, uniqueIndex, varchar, text } from 'drizzle-orm/mysql-core';



export const UserTable = mysqlTable('users',
{
id: varchar('id', { length: 36 }).notNull().default('uuid-will-be-generated').primaryKey(),
createdAt: timestamp('createdAt').defaultNow().notNull(),
updatedAt: timestamp('updatedAt').defaultNow().notNull(),
username: varchar('username', { length: 60 }).notNull().default(''),
email: varchar('email', { length: 256 }).notNull().default(''),
realName: varchar('realName', { length: 60 }).notNull().default(''),
password: varchar('password', { length: 256 }).notNull().default('no-password-specified'),
passwordType: mysqlEnum('passwordType', ['bcrypt']).notNull().default('bcrypt'),
useAsDisplayName: mysqlEnum('useAsDisplayName', ['username','email','realName']).notNull().default('username'),
admin: boolean('admin').notNull().default(false),
developer: boolean('developer').notNull().default(false)
},
(table) => ({
usernameIndex: uniqueIndex('usernameIndex').on(table.username),
emailIndex: uniqueIndex('emailIndex').on(table.email)
})
);


export type UserModel = typeof UserTable;
export type UserSelect = InferModel<UserModel, 'select'>
export type UserInsert = InferModel<UserModel, 'insert'>
The resulting UserModel
type UserModel = MySqlTable<{
name: "users";
columns: {
id: MySqlColumn<{
data: string;
driverParam: string | number;
hasDefault: true;
notNull: true;
tableName: "users";
}>;
createdAt: MySqlColumn<{
data: Date;
driverParam: string | number;
hasDefault: true;
notNull: true;
tableName: "users";
}>;
... 8 more ...;
developer: MySqlColumn<...>;
};
}> & {
...;
}
type UserModel = MySqlTable<{
name: "users";
columns: {
id: MySqlColumn<{
data: string;
driverParam: string | number;
hasDefault: true;
notNull: true;
tableName: "users";
}>;
createdAt: MySqlColumn<{
data: Date;
driverParam: string | number;
hasDefault: true;
notNull: true;
tableName: "users";
}>;
... 8 more ...;
developer: MySqlColumn<...>;
};
}> & {
...;
}
the resulting UserSelect model:
type UserSelect = {
id: string;
createdAt: Date;
updatedAt: Date;
username: string;
email: string;
realName: string;
password: string;
passwordType: "bcrypt";
useAsDisplayName: "username" | "email" | "realName";
admin: boolean;
developer: boolean;
}
type UserSelect = {
id: string;
createdAt: Date;
updatedAt: Date;
username: string;
email: string;
realName: string;
password: string;
passwordType: "bcrypt";
useAsDisplayName: "username" | "email" | "realName";
admin: boolean;
developer: boolean;
}
bloberenober
bloberenoberā€¢16mo ago
You are trying to pass the selection model into the insert function. You need to pass the insertion model.
JT
JTā€¢16mo ago
It makes no difference, basically the same error Also, the selection model is the complete version of the insert model, meaning that it has everything thus it should work anyway Here's the error with the insert model used:
Argument of type 'ModelMap[T]["insert"]' is not assignable to parameter of type 'MySqlInsertValue<ModelMap[T]["model"]>'.
Type '{ id?: string | undefined; createdAt?: Date | undefined; updatedAt?: Date | undefined; username?: string | undefined; email?: string | undefined; realName?: string | undefined; ... 4 more ...; developer?: boolean | undefined; } | { ...; }' is not assignable to type 'MySqlInsertValue<ModelMap[T]["model"]>'.
Type '{ id?: string | undefined; createdAt?: Date | undefined; updatedAt?: Date | undefined; username?: string | undefined; email?: string | undefined; realName?: string | undefined; ... 4 more ...; developer?: boolean | undefined; }' is not assignable to type 'MySqlInsertValue<ModelMap[T]["model"]>'
Argument of type 'ModelMap[T]["insert"]' is not assignable to parameter of type 'MySqlInsertValue<ModelMap[T]["model"]>'.
Type '{ id?: string | undefined; createdAt?: Date | undefined; updatedAt?: Date | undefined; username?: string | undefined; email?: string | undefined; realName?: string | undefined; ... 4 more ...; developer?: boolean | undefined; } | { ...; }' is not assignable to type 'MySqlInsertValue<ModelMap[T]["model"]>'.
Type '{ id?: string | undefined; createdAt?: Date | undefined; updatedAt?: Date | undefined; username?: string | undefined; email?: string | undefined; realName?: string | undefined; ... 4 more ...; developer?: boolean | undefined; }' is not assignable to type 'MySqlInsertValue<ModelMap[T]["model"]>'
bloberenober
bloberenoberā€¢16mo ago
Seems like you have not a single type in your ModelMap[T]['insert'], but a union, as indicated by the inferred type here. So there're potentially multiple unrelated types there (probably from different tables, if your ModelMap type is declared incorrectly?), which you are trying to pass as an insertion value for a specific table. Hence the error.
bloberenober
bloberenoberā€¢16mo ago
In any case, it's impossible to debug this error without looking at how your types are defined and what they are resolving into. So if you could put the complete code somewhere for me to look, I can provide you more insights on what might be wrong. For now, it seems like the issue is with how you defined your types.
JT
JTā€¢16mo ago
yeah, i said there was a union in there, as that's the point of the type map. but the T disambiguates it. i'll work on breaking this into a simpler example so you don't need to read through so much code i should have done that from the start i'm sorry
bloberenober
bloberenoberā€¢16mo ago
I'm fine reading through that code, it's just the samples you've sent doesn't help with identifying the issue I need to look at how you're building and using the ModelMap type
JT
JTā€¢16mo ago
Gist
demonstrates a problem I'm having passing drizzle props to a generi...
demonstrates a problem I'm having passing drizzle props to a generic doit() function. the T should disambiguate, but it doesn't - drizzle-model-map.ts
JT
JTā€¢16mo ago
this gist simplifies down the problem
JT
JTā€¢16mo ago
JT
JTā€¢16mo ago
^ you get that problem in vs code
bloberenober
bloberenoberā€¢16mo ago
OK, I figured it out. Your thinking is correct, it's just that TypeScript is not that clever to resolve this logic on its own. In your example, the table and props arguments are inferred independently from each other, so TypeScript can't verify that they belong to the same table and thus can be used together. I've rewritten your types a bit so that TS can understand what you're trying to do. https://gist.github.com/dankochetov/0ecf316dfed098a793deef69637361f1
Gist
demonstrates a problem I'm having passing drizzle props to a generi...
demonstrates a problem I'm having passing drizzle props to a generic doit() function. the T should disambiguate, but it doesn't - drizzle-model-map.ts
bloberenober
bloberenoberā€¢16mo ago
It may seem like it does the exact same thing, but it works differently under the hood. Honestly, if you ask me why, I couldn't answer - I just tried it and it worked. šŸ¤·ā€ā™‚ļø
JT
JTā€¢16mo ago
heh. typescript is so damn weird! thank you so much
bloberenober
bloberenoberā€¢16mo ago
np
JT
JTā€¢16mo ago
holy cow that breaks my code so much...i guess i've got a lot of ts debugging to work out. not your problem, but thanks for solving this one. just ran into another weird TS quirk. it thinks this is bad:
let out: Describe<T> = { props: { id : this.get('id') } };
let out: Describe<T> = { props: { id : this.get('id') } };
but this is good:
let out: Describe<T> = { props: {} };
out.props.id = this.get('id');
let out: Describe<T> = { props: {} };
out.props.id = this.get('id');
They are effectively the same thing TS! šŸ™‚