镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

[PERFORMANCE]: TS takes several seconds to validate types resulting in poor DX #54939

Description

@mhevery

Bug Report

🔎 Search Terms

🕗 Version & Regression Information

  • This is a slow type verification leading to bad DX.

⏯ Playground Link

Playground link with relevant code

Related issue: drizzle-team/drizzle-orm#870

💻 Code

// We can quickly address your report if:
//  - The code sample is short. Nearly all TypeScript bugs can be demonstrated in 20-30 lines of code!
//  - It doesn't use external libraries. These are often issues with the type definitions rather than TypeScript bugs.
//  - The incorrectness of the behavior is readily apparent from reading the sample.
// Reports are slower to investigate if:
//  - We have to pare too much extraneous code.
//  - We have to clone a large repo and validate that the problem isn't elsewhere.
//  - The sample is confusing or doesn't clearly demonstrate what's wrong.

I understand that this issue uses an external library, drizzle which is the source of performance problems. However, I think it would be useful for the TS team to see how people use types in real world and to see if either the drizzle team can fix the types to be better performant or the TS team can do something to better handle the use cases used by drizzle.

import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';

/**
 * This is a reproduction of slow TypeScript type validation with Drizzle.
 *
 * Steps to reproduce:
 * 1. Change "EDIT THIS" line and change `2` to `"2"` (add qoutes around the number)
 *    and save the file
 * 2. Because we have changed number to string the TS will now underline the line as
 *    being invalid.
 * 3. Go back and forth between the `2` and `"2"` and notice that there is a several
 *    second delay everytime you change source code and TS either has to add or remove
 *    the red underline. (TS Error)
 * 4. Once you establish a beseline feel to how long it takes TS to add/removo an error,
 *    comment out the `delayCount`/`latencyCount` block and repeat the steps
 * 5. Notice that now the add/remove error is instant.
 */

const x: number = 2; // EDIT THIS

export const edgeTable = sqliteTable('edges', {
  id: integer('id').primaryKey(),
  manifestHash: text('manifest_hash').notNull(),
  from: text('from'),
  to: text('to').notNull(),
  interaction: integer('interaction').notNull(),

  // COMMENT OUT THE LINES BELOW
  //////////////////////////////
  // START

  delayCount00: integer('delay_count_00').notNull(),
  delayCount01: integer('delay_count_01').notNull(),
  delayCount02: integer('delay_count_02').notNull(),
  delayCount03: integer('delay_count_03').notNull(),
  delayCount04: integer('delay_count_04').notNull(),
  delayCount05: integer('delay_count_05').notNull(),
  delayCount06: integer('delay_count_06').notNull(),
  delayCount07: integer('delay_count_07').notNull(),
  delayCount08: integer('delay_count_08').notNull(),
  delayCount09: integer('delay_count_09').notNull(),
  delayCount10: integer('delay_count_10').notNull(),
  delayCount11: integer('delay_count_11').notNull(),
  delayCount12: integer('delay_count_12').notNull(),
  delayCount13: integer('delay_count_13').notNull(),
  delayCount14: integer('delay_count_14').notNull(),
  delayCount15: integer('delay_count_15').notNull(),
  delayCount16: integer('delay_count_16').notNull(),
  delayCount17: integer('delay_count_17').notNull(),
  delayCount18: integer('delay_count_18').notNull(),
  delayCount19: integer('delay_count_19').notNull(),
  delayCount20: integer('delay_count_20').notNull(),
  delayCount21: integer('delay_count_21').notNull(),
  delayCount22: integer('delay_count_22').notNull(),
  delayCount23: integer('delay_count_23').notNull(),
  delayCount24: integer('delay_count_24').notNull(),
  delayCount25: integer('delay_count_25').notNull(),
  delayCount26: integer('delay_count_26').notNull(),
  delayCount27: integer('delay_count_27').notNull(),
  delayCount28: integer('delay_count_28').notNull(),
  delayCount29: integer('delay_count_29').notNull(),
  delayCount30: integer('delay_count_30').notNull(),
  delayCount31: integer('delay_count_31').notNull(),
  delayCount32: integer('delay_count_32').notNull(),
  delayCount33: integer('delay_count_33').notNull(),
  delayCount34: integer('delay_count_34').notNull(),
  delayCount35: integer('delay_count_35').notNull(),
  delayCount36: integer('delay_count_36').notNull(),
  delayCount37: integer('delay_count_37').notNull(),
  delayCount38: integer('delay_count_38').notNull(),
  delayCount39: integer('delay_count_39').notNull(),
  delayCount40: integer('delay_count_40').notNull(),
  delayCount41: integer('delay_count_41').notNull(),
  delayCount42: integer('delay_count_42').notNull(),
  delayCount43: integer('delay_count_43').notNull(),
  delayCount44: integer('delay_count_44').notNull(),
  delayCount45: integer('delay_count_45').notNull(),
  delayCount46: integer('delay_count_46').notNull(),
  delayCount47: integer('delay_count_47').notNull(),
  delayCount48: integer('delay_count_48').notNull(),
  delayCount49: integer('delay_count_49').notNull(),
  latencyCount00: integer('latency_count_00').notNull(),
  latencyCount01: integer('latency_count_01').notNull(),
  latencyCount02: integer('latency_count_02').notNull(),
  latencyCount03: integer('latency_count_03').notNull(),
  latencyCount04: integer('latency_count_04').notNull(),
  latencyCount05: integer('latency_count_05').notNull(),
  latencyCount06: integer('latency_count_06').notNull(),
  latencyCount07: integer('latency_count_07').notNull(),
  latencyCount08: integer('latency_count_08').notNull(),
  latencyCount09: integer('latency_count_09').notNull(),
  latencyCount10: integer('latency_count_10').notNull(),
  latencyCount11: integer('latency_count_11').notNull(),
  latencyCount12: integer('latency_count_12').notNull(),
  latencyCount13: integer('latency_count_13').notNull(),
  latencyCount14: integer('latency_count_14').notNull(),
  latencyCount15: integer('latency_count_15').notNull(),
  latencyCount16: integer('latency_count_16').notNull(),
  latencyCount17: integer('latency_count_17').notNull(),
  latencyCount18: integer('latency_count_18').notNull(),
  latencyCount19: integer('latency_count_19').notNull(),
  latencyCount20: integer('latency_count_20').notNull(),
  latencyCount21: integer('latency_count_21').notNull(),
  latencyCount22: integer('latency_count_22').notNull(),
  latencyCount23: integer('latency_count_23').notNull(),
  latencyCount24: integer('latency_count_24').notNull(),
  latencyCount25: integer('latency_count_25').notNull(),
  latencyCount26: integer('latency_count_26').notNull(),
  latencyCount27: integer('latency_count_27').notNull(),
  latencyCount28: integer('latency_count_28').notNull(),
  latencyCount29: integer('latency_count_29').notNull(),
  latencyCount30: integer('latency_count_30').notNull(),
  latencyCount31: integer('latency_count_31').notNull(),
  latencyCount32: integer('latency_count_32').notNull(),
  latencyCount33: integer('latency_count_33').notNull(),
  latencyCount34: integer('latency_count_34').notNull(),
  latencyCount35: integer('latency_count_35').notNull(),
  latencyCount36: integer('latency_count_36').notNull(),
  latencyCount37: integer('latency_count_37').notNull(),
  latencyCount38: integer('latency_count_38').notNull(),
  latencyCount39: integer('latency_count_39').notNull(),
  latencyCount40: integer('latency_count_40').notNull(),
  latencyCount41: integer('latency_count_41').notNull(),
  latencyCount42: integer('latency_count_42').notNull(),
  latencyCount43: integer('latency_count_43').notNull(),
  latencyCount44: integer('latency_count_44').notNull(),
  latencyCount45: integer('latency_count_45').notNull(),
  latencyCount46: integer('latency_count_46').notNull(),
  latencyCount47: integer('latency_count_47').notNull(),
  latencyCount48: integer('latency_count_48').notNull(),
  latencyCount49: integer('latency_count_49').notNull(),

  // END
});

🙁 Actual behavior

Slow (2-3 second) TS validation time. This interferes with code-completion and makes for a bad DX.

🙂 Expected behavior

Type validation should stay performant.

Activity

  1. RyanCavanaugh commented on Jul 10, 2023

    @RyanCavanaugh
    Member

    Please provide a self-contained repro; we do not have time to dig into large external typings to figure out if they're written performantly or not.

  2. jakebailey commented on Aug 3, 2023

    @jakebailey
    Member

    Miško Hevery (@mhevery) #55224 will mitigate a good bit of this. Not all, but in your specific example's case about 50% overall of a tsc run, which should translate to a higher fraction during editing as tsc has to parse/bind anew (then emit), while the editor won't do any of that.

    If you want to try it out before it's in a release, you can install the package from #55224 (comment) and then switch your editor to use it instead of what's built into VS Code (or wait for a nightly).

  3. luxaritas commented on Aug 22, 2023

    @luxaritas

    Jake Bailey (@jakebailey) and I had further discussion around this tonight. Both his PR for recursiveTypeRelatedTo and drizzle's recent typing updates removing HKTs have made a significant improvement, however I'm still seeing situations where intellisense is slowing down quite a bit (though, notably, not tsc). Moving from 5.1 to nightly in VS code on my "real" project I did see the reported ~50% speedup - it's possible the issue have may moved somewhere else now (and, if so, may be worth a separate issue, but starting with a comment here).

    The reproduction I have is similar to the original here, though larger: https://stackblitz.com/edit/stackblitz-starters-uaglzu?file=index.ts Try waiting at every . of db.query.edgeTable2.findFirst, and you'll see the slowdown.

    I'm actively working on trying to build out a more minimal reproduction extracted from drizzle. So far a big thing I've noticed is that a big contributor is the extends check in the key portion of the following mapped type (ExtractTablesWithRelations): [K in keyof TSchema as TSchema[K] extends Table ? K : never]: TSchema[K] extends Table ? {...} : never. Drizzle's extensive use of nested generics (including each column having a high likelihood of having a unique type due to the column name being embedded as part of the type) is assuredly not helping here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs More InfoThe issue still hasn't been fully clarified

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions