Skip to content

[BUG]: TypeScript error on optional fields during update (and insert) operation #2654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
DavidHavl opened this issue Jul 18, 2024 · 60 comments
Closed
Labels
bug Something isn't working priority Will be worked on next

Comments

@DavidHavl
Copy link

DavidHavl commented Jul 18, 2024

What version of drizzle-orm are you using?

0.32.0

What version of drizzle-kit are you using?

0.23.0

Describe the Bug

I've encountered a TypeScript error when trying to update an optional field using the set method. The error occurs only for optional fields, while required fields work as expected.

Steps to reproduce:

  • Define a table with both required and optional fields:
export const TasksTable = sqliteTable(
  'tasks',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => createId()),
    title: text('title').notNull(),
    description: text('description')
  },
);
  • Attempt to update the table with both required and optional fields:
const result = await db
  .update(TasksTable)
  .set({
    title: 'sdfg',
    description: 'asdf'
  });

Expected behavior:
The update operation should work without any TypeScript errors, as all fields being updated are defined in the table schema.

Actual behavior:
TypeScript throws an error for the optional 'description' field:
"Object literal may only specify known properties, and 'description' does not exist in type"
The error does not occur for the required 'title' field.

Additional notes:

  • The error only occurs for optional fields (those without .notNull())
  • Using as any type assertion works as a temporary workaround, but it's not a proper solution

Please let me know if you need any additional information or clarification to investigate this issue.

Expected behavior

The update operation should work without any TypeScript errors, as all fields being updated are defined in the table schema.

Environment & setup

Database: SQLite
Node: 20
Typescript: 5.3.3

@DavidHavl DavidHavl added the bug Something isn't working label Jul 18, 2024
@lmcneel
Copy link

lmcneel commented Jul 21, 2024

We encountered this same issue with:

Database: Postgresql
Node: 20
TypeScript: 5.5.3

We attempted to pin to a Drizzle ORM 0.31.0 to wait for a fix and discovered that we can't use Drizzle Kit if we do that.
Drizzle Kit prints that it needs to be updated and won't generate migrations.
The new version of Drizzle Kit requires Drizzle ORM > 0.32.0.

@tirth0
Copy link

tirth0 commented Jul 23, 2024

Experiencing the same issue.

@Jean-CharlesTerrillon
Copy link

Same here

I suggest to use inferSelect instead of inferInsert, see below

export type PgUpdateSetSource = {
[Key in keyof TTable['$inferSelect']]?: GetColumnData<TTable['_']['columns'][Key]> | SQL;
} & {};

@murilo-kendjy
Copy link

I was facing the same problem when upgrading to version >0.31.4, there's a post in discord where rphlmr said that you need strict mode on your tsconfig.json. That solved for me.

ps: I needed to remove all other strict tags and let only strict tag true.

@eliellis
Copy link

eliellis commented Jul 24, 2024

I was facing the same problem when upgrading to version >0.31.4, there's a post in discord where rphlmr said that you need strict mode on your tsconfig.json. That solved for me.

ps: I needed to remove all other strict tags and let only strict tag true.

While this does seem to fix the issue, I'd argue that this is an incredibly disruptive change to make to a project when it was not a requirement before now. Hopefully this can be resolved without resorting to flipping strict: true in projects that have been previously successfully using Drizzle without this compiler flag.

@mion-kr
Copy link

mion-kr commented Aug 1, 2024

same issue

@khanhquocnguyen
Copy link

I have the same issue.

Environment:

  • OS: MacOS
  • Database: PostgreSQL
  • drizzle-orm version: 0.32.2
  • node version: v22.5.1
  • typescript version: 5.4.5

Temporary workaround:
Add the following comment line above the insert command.
// @ts-ignore: Unreachable code error

@Anuolu-2020
Copy link

Has there being any fix to this bug? I don't want to turn on strict mode in my tsconfig.json

@anishkumar127
Copy link

Same issue please give me solution. any update to fix this issue ?

@sameedahri
Copy link

sameedahri commented Sep 18, 2024

Any solution, I am also facing same issue?

@sameedahri
Copy link

I found the solution guys. Just go to the tsconfig.json file, in the compiler options object add below line.
"strictNullChecks": true,

@anishkumar127
Copy link

I found the solution guys. Just go to the tsconfig.json file, in the compiler options object add below line. "strictNullChecks": true,

this also not working!

@sameedahri
Copy link

sameedahri commented Sep 23, 2024

Are you guys sure? Because I got stuck for so many days, and this thing solves the issue, I can also insert nullable values and typescript also auto complete table variable name

@sameedahri
Copy link

Can you guys share you tsconfig file?

@leandromatos
Copy link

Unfortunately, I decided not to upgrade the drizzle-orm and drizzle-kit packages since I don't want to change the typescript configs, which would cause a lot of other troubles in the project.

Here, I'm using drizzle-kit@^0.22.8 and drizzle-orm@^0.31.4.

@jabreland
Copy link

Changing it to strict worked for me to. (Postrgres and Drizzle-orm 0.33) I was confused because it was working just fine in another project but I had already turned strict on for Acktypes

@mogery
Copy link

mogery commented Sep 27, 2024

That's not a fix. I'm not going to rewrite a 20k LoC codebase to work with strict null checking just to make this work.

@leandromatos
Copy link

The problem with using the strict option on TypeScript is that, depending on the size of your code base, you will be forced to update a lot of things. This is really annoying.

@naourass
Copy link

naourass commented Oct 3, 2024

I was going crazy after updating Drizzle to the latest version. Fortunately I've found that thread, maybe adding a notice in docs beside installation section would help users that were using Drizzle without ts strict mode.

@lcptrs
Copy link

lcptrs commented Oct 8, 2024

It seems to have gotten better with the 0.34 release. In our codebase we only have the issue remaining on the set parameter of the onConflictDoUpdate which can be easily worked around with a cast as Partial<typeof table.$inferInsert>🎉

@leandromatos
Copy link

Unfortunately, @lcptrs, this workaround will add another problem. The result of an insert operation's inference returns the optional and required fields defined in the database. Your suggestion will make all the properties optional.

I believe this problem won't be fixed in the near future. It seems to be a mix of the natural evolution of TypeScript and its config and the evolution of other dependencies. I'm not sure about that, but if the solution is to use strict: true on the tsconfig.json, I think it is because some dependencies use the strict property, and the types are conflicting.

I will probably give up and put strict: true on my project, fix all the TypeScript problems caused by this config, and finally update the new versions of the Drizzle packages.

@L-Mario564 L-Mario564 added the priority Will be worked on next label Oct 23, 2024
@0xshc
Copy link

0xshc commented Nov 11, 2024

is there any update on this? still not fixed.

database: postgres
node: 20
typescript: 5.6.3
drizzle-orm: 0.36.1
drizzle-kit: 0.28.0

@NiazMorshed2007
Copy link

same problem :(

drizzle-orm: 0.36.1
drizzle-kit: 0.28.0

@AlexBlokh
Copy link
Contributor

well, that's a very tricky one, we've spent half a day with @Sukairo-02 trying to figure out what type in particular is responsible for that

strict: true indeed fixes it, with strict: false there're 2 more ts flags that fix it, yet we will make sure it works out of the box for you

@AlexBlokh
Copy link
Contributor

alright, landing in beta today - #3542

@erikmunson
Copy link

this should be reopened as the change in 0.36.4 that claimed to address the problem was not actually a fix. this makes using drizzle in existing projects that don't already have strict mode turned on very difficult and i've had to pin my version of drizzle back to before this broke. it's not practical to change an entire existing project over to strict mode.

@dexxiez
Copy link

dexxiez commented Dec 24, 2024

it's not practical to change an entire existing project over to strict mode.

I agree with this as Drizzle is "marketed" as a library and not a framework. On their website they say

With data frameworks you have to build projects around them and not with them.

Turning on strict mode and needing to do massive refactors to work with it, might as well be a framework for all intensive purposes.

@ctrhub
Copy link

ctrhub commented Dec 25, 2024

Version 0.38.2. This issue is really annoying. In my case(nestjs in turborepo), even "strict": true doesn't help.
In the end, I used @dexxiez's approach (by the way, thanks to @dexxiez).

Image

Screenshot 2024-12-25 at 20 46 51

@ahsankhan201010
Copy link

still facing this issue - v0.38.3

@iamsaebil
Copy link

It's still happening - 0.38.3

@AdamRomyn
Copy link

AdamRomyn commented Jan 15, 2025

Hey all this is not a long term fix but made my code seem somewhat okay

I just used a Partial class to make the typescript compiler relax and then also you get intellisense but careful as it won't enforce your structure on inserts.

const instagramProfileUpdate: Partial<InstagramProfile> = {
            sentFirstMessage: true
}
await db.update(instagramProfiles).set(instagramProfileUpdate).where(eq(instagramProfiles.instagramId, user.instagramId));

The "strictNullChecks": true worked for me but its a lot of work if you haven't started the project with that compiler setting on.

@rochajulian
Copy link

this issue should not be closed. it is still ongoing

@Krimax0
Copy link

Krimax0 commented Jan 21, 2025

Exactly the same problem
"drizzle-orm": "^0.38.3",

@olegklimakov
Copy link

Still the same in 0.38.4

@trickroll
Copy link

Also facing this issue with 0.39.3

@MatthewRandle
Copy link

Facing the same issue with v0.39.3.

Getting this error which shows the property does exist:

Object literal may only specify known properties, and 'restaurantID' does not exist in type '{ name: string | SQL<unknown> | Placeholder<string, any>; restaurantID: number | SQL<unknown> | Placeholder<string, any>...

Added a temporary // @ts-expect-error to suppress the error

@cristiano-linvix
Copy link

Same problem here, with v0.39.3.

@lcptrs
Copy link

lcptrs commented Feb 21, 2025

@AlexBlokh sorry to ping you, but I want to manage my expectations on this one:
Are there any plans to revisit this issue? Or is there any other issue that relates to this that I'm missing?

@gemue-parndt
Copy link

gemue-parndt commented Mar 21, 2025

This issue is so annyoing - why is it closed ?!
Hard to think using drizzle when bugs like this won't get fixed soon...

Still happening in v.0.40.1

@Sukairo-02 Sukairo-02 reopened this Mar 21, 2025
@cristiano-linvix
Copy link

The issue still persists.

@JuanPabloCano
Copy link

Still happening in v.0.41.0

@Sukairo-02
Copy link
Collaborator

Published experimental fix in drizzle-orm@no-strict-type-infer-fix branch, however, given the history of the issue, we're going to need feedback on fix's functionality. Please, report whether it works for you or not, and, if possible, attach broken table and project's tsconfig if it doesn't work.

@Prince-Mendiratta
Copy link

Working for me with drizzle-orm@no-strict-type-infer-fix. tweetCount in newTrend was throwing error previously.

{
  "compilerOptions": {
    /* Base Options: */
    "esModuleInterop": true,
    "skipLibCheck": true,
    "target": "es2022",
    "allowJs": true,
    "resolveJsonModule": true,
    "moduleDetection": "force",
    "isolatedModules": true,
    "strictNullChecks": false,

    /* Strictness */
    "strict": true,
    "checkJs": true,

    /* Bundled projects */
    "lib": ["dom", "dom.iterable", "ES2022"],
    "noEmit": true,
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "jsx": "preserve",
    "plugins": [{ "name": "next" }],
    "incremental": true,

    /* Path Aliases */
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": [
    ".eslintrc.cjs",
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    "**/*.cjs",
    "**/*.js",
    ".next/types/**/*.ts",
    "src/styles/global.css"
  ],
  "exclude": [
    "node_modules/*",
    ".next",
    "out",
    "build",
    "dist",
    "supabase"
  ]
}
import { pgTable, serial, text, timestamp, integer, boolean, uniqueIndex, unique } from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
import { InferInsertModel, InferSelectModel } from "drizzle-orm";

// Users table
export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  username: text('username').notNull(),
  email: text('email').notNull(),
  profileImage: text('profile_image'),
  bio: text('bio'),
  isVerified: boolean('is_verified').default(false),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull(),
}, (table) => {
  return {
    usernameIdx: uniqueIndex('username_idx').on(table.username),
    emailIdx: uniqueIndex('email_idx').on(table.email),
  };
});

// Tweets table
export const tweets = pgTable('tweets', {
  id: serial('id').primaryKey(),
  content: text('content').notNull(),
  userId: integer('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  image: text('image'),
  likes: integer('likes').default(0),
  retweets: integer('retweets').default(0),
  replies: integer('replies').default(0),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull(),
});

// Likes table - junction between users and tweets
export const likes = pgTable('likes', {
  id: serial('id').primaryKey(),
  userId: integer('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  tweetId: integer('tweet_id').notNull().references(() => tweets.id, { onDelete: 'cascade' }),
  createdAt: timestamp('created_at').defaultNow().notNull(),
}, (table) => {
  return {
    userTweetIdx: unique().on(table.userId, table.tweetId)
  };
});

// Follows table - for following relationships
export const follows = pgTable('follows', {
  id: serial('id').primaryKey(),
  followerId: integer('follower_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  followingId: integer('following_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  createdAt: timestamp('created_at').defaultNow().notNull(),
}, (table) => {
  return {
    followerFollowingIdx: unique().on(table.followerId, table.followingId)
  };
});

// Trends table - for trending topics
export const trends = pgTable('trends', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  category: text('category').notNull(),
  tweetCount: integer('tweet_count').default(0),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull(),
});

// Define relations
export const usersRelations = relations(users, ({ many }) => ({
  tweets: many(tweets),
  likes: many(likes),
  followedBy: many(follows, { relationName: "followedBy" }),
  following: many(follows, { relationName: "following" }),
}));

export const tweetsRelations = relations(tweets, ({ one, many }) => ({
  author: one(users, {
    fields: [tweets.userId],
    references: [users.id],
  }),
  likes: many(likes),
}));

export const likesRelations = relations(likes, ({ one }) => ({
  user: one(users, {
    fields: [likes.userId],
    references: [users.id],
  }),
  tweet: one(tweets, {
    fields: [likes.tweetId],
    references: [tweets.id],
  }),
}));

export const followsRelations = relations(follows, ({ one }) => ({
  follower: one(users, {
    fields: [follows.followerId],
    references: [users.id],
    relationName: "following",
  }),
  following: one(users, {
    fields: [follows.followingId],
    references: [users.id],
    relationName: "followedBy",
  }),
}));

// Type definitions for easier use with API routes
export type User = InferSelectModel<typeof users>;
export type NewUser = InferInsertModel<typeof users>;

export type Tweet = InferSelectModel<typeof tweets>;
export type NewTweet = InferInsertModel<typeof tweets>;

export type Like = InferSelectModel<typeof likes>;
export type NewLike = InferInsertModel<typeof likes>;

export type Follow = InferSelectModel<typeof follows>;
export type NewFollow = InferInsertModel<typeof follows>;

export type Trend = InferSelectModel<typeof trends>;
export type NewTrend = InferInsertModel<typeof trends>;
import { NextResponse } from "next/server";
import { db } from "@/db";
import { trends, NewTrend } from "@/db/schema";
import { desc } from "drizzle-orm";

export async function GET() {
  try {
    const allTrends = await db.select().from(trends).orderBy(desc(trends.tweetCount));
    
    return NextResponse.json(allTrends);
  } catch (error) {
    console.error("Error fetching trends:", error);
    return NextResponse.json(
      { error: "Failed to fetch trends" },
      { status: 500 }
    );
  }
}

export async function POST(request: Request) {
  try {
    const body = await request.json();
    const { title, category, tweetCount } = body;
    
    if (!title || !category) {
      return NextResponse.json(
        { error: "Title and category are required" },
        { status: 400 }
      );
    }
    
    const newTrend: NewTrend = {
      title,
      category,
      tweetCount: tweetCount || 0,
    };
    
    const insertedTrend = await db.insert(trends).values(newTrend).returning();
    
    return NextResponse.json(insertedTrend[0]);
  } catch (error) {
    console.error("Error creating trend:", error);
    return NextResponse.json(
      { error: "Failed to create trend" },
      { status: 500 }
    );
  }
}

@IsaacInsoll
Copy link

That's great to hear.

I went the other way and turned in strict mode and worked through the 1000 errors from the rest of my codebase 🤣

@DavidHavl
Copy link
Author

Published experimental fix in drizzle-orm@no-strict-type-infer-fix branch, however, given the history of the issue, we're going to need feedback on fix's functionality. Please, report whether it works for you or not, and, if possible, attach broken table and project's tsconfig if it doesn't work.

I have not had a chance to look at this issue since last year, but I definitely think changing tsconfig or expecting user to alter their tsconfig is not (should not) be the solution.
It should be done by changing the set method signature or making it work from that side. Of course changing the method signature most likely means it will need a minor/major version change.

@AlexBlokh
Copy link
Contributor

@DavidHavl that's exactly what we've done, we don't expect developers to change config, we want to provide as much of an out of the box as possible, when it comes to types and tsconfig

we just need feedback from developers who've had issues, thanks

@DavidHavl
Copy link
Author

@AlexBlokh Oh I see, that is excellent!
Apologies for the misunderstanding. I should have had a closer look at the code.
I'll try and test it out in the next couple of days.

Sukairo-02 added a commit that referenced this issue Apr 22, 2025
@Sukairo-02 Sukairo-02 mentioned this issue Apr 22, 2025
AndriiSherman added a commit that referenced this issue Apr 24, 2025
* Add support for cross join (#1669)

* Add cross join support

* Add cross join tests

* Updated docs links (docs TBD)

* Merged cross join function types into join function types, fixed missing cases for `cross` join type, ordered `crossJoin` test queries

* `onIndex` support for `MySQL` `crossJoin`, fixed lack of `onIndex` param description in JSDoc, additional tests, disabled test case failing due to driver's issue

---------

Co-authored-by: Sukairo-02 <[email protected]>

* Export `PgTextBuilderInitial` type (#1286)

* MySQL: Removed .fullJoin() from MySQL Api (#1351)

Added new type MySqlJoinType which excludes the option for full joins and is used in the MySQL query builder createJoin function instead of the
regular JoinType.
Also removed two MySQL select type-tests which included the fullJoin function

Co-authored-by: Sukairo-02 <[email protected]>

* [Pg] Add support for left and inner lateral join in postgres (#1079)

* feat: added support for left and inner lateral join in postgres

Signed-off-by: Alex Vukadinov <[email protected]>

* chore: updated method descriptions on the new joins

Signed-off-by: Alex Vukadinov <[email protected]>

* GH-420 allowing sql inside lateral joins and revert package.json

* GH-420 reverted package.json empty line at the end

* Changed JSDoc [WIP], reversed check

---------

Signed-off-by: Alex Vukadinov <[email protected]>
Co-authored-by: Sukairo-02 <[email protected]>

* Completed `left`, `inner`, `cross` lateral joins in `postgresql`, `mysql`, `gel`, `singlestore`, disabled implicit `schema.table` prefixes in column selections on single table selections for `gel` due to possible errors in subqueries on column name matches in inner and outer queries, added\altered related tests

* [SingleStore] Add Connection Attributes and Fix Options (#4417)

* add connection attributes to SingleStore driver connection

* fix allowing connection strings/options rather than external pool

---------

Co-authored-by: Andrii Sherman <[email protected]>

* Fix for #2654, related tests (#4353)

* Dprint

* Bumped version, added changelog, fixed broken test case

* fix(3554) Correct spelling of `nowait` flag (#3555)

* Fixed `nowait` in `Gel`, `SingleStore`, added change to changelog

* Updated changelog

* Fixed broken test case

* Fixed `nowait` tests in `bun-sql`, `mysql-prefixed`

* Add changelog updates

---------

Signed-off-by: Alex Vukadinov <[email protected]>
Co-authored-by: L-Mario564 <[email protected]>
Co-authored-by: Dan Imhoff <[email protected]>
Co-authored-by: Itay Ben-Ami <[email protected]>
Co-authored-by: Alex Vukadinov <[email protected]>
Co-authored-by: Mitchell Adair <[email protected]>
Co-authored-by: Andrii Sherman <[email protected]>
Co-authored-by: Jacob Elder <[email protected]>
@AndriiSherman
Copy link
Member

Should be fixed in [email protected]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working priority Will be worked on next
Projects
None yet
Development

No branches or pull requests