Skip to content

Poor TS performance in TS 4.5 leading to errors #2177

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
JeongJuhyeon opened this issue Dec 20, 2021 · 12 comments
Closed

Poor TS performance in TS 4.5 leading to errors #2177

JeongJuhyeon opened this issue Dec 20, 2021 · 12 comments

Comments

@JeongJuhyeon
Copy link

JeongJuhyeon commented Dec 20, 2021

Objection: 3.0.0
Knex: 0.95.14
Typescript: 4.5.4

Minimal reproducable example:

import { Model, Page, QueryBuilder } from 'objection';
export class ExtendedQueryBuilder<
  M extends Model,
  R = M[]
> extends QueryBuilder<M, R> {
  // These are necessary. You can just copy-paste them and change the name of the query builder class.
  ArrayQueryBuilderType!: ExtendedQueryBuilder<M, M[]>;
  SingleQueryBuilderType!: ExtendedQueryBuilder<M, M>;
  MaybeSingleQueryBuilderType!: ExtendedQueryBuilder<M, M | undefined>;
  NumberQueryBuilderType!: ExtendedQueryBuilder<M, number>;
  PageQueryBuilderType!: ExtendedQueryBuilder<M, Page<M>>;
}

export class BaseModel extends Model {
  QueryBuilderType!: ExtendedQueryBuilder<this>;
  static QueryBuilder = ExtendedQueryBuilder;
}

export default class Person extends BaseModel {
  id!: number;

  static get tableName() {
    return 'map';
  }
}

async function getPerson() {
  const k = await Person.query(); // k: any
}

Inference fails and "k" becomes "any". On 4.5.2 it also fails most of the time, just hanging instead of inferring "any". It also fails in the 4.6 dev build.

TS Server log reveals what looks like infinite recursion going on:

Info 71   [16:28:04.911] response:
    {"seq":0,"type":"response","command":"getApplicableRefactors","request_seq":7,"success":true,"body":[{"name":"Convert export","description":"Convert default export to named export","actions":[{"name":"Convert default export to named export","description":"Convert default export to named export","kind":"refactor.rewrite.export.named","notApplicableReason":"Could not find export statement"},{"name":"Convert named export to default export","description":"Convert named export to default export","kind":"refactor.rewrite.export.default","notApplicableReason":"Could not find export statement"}]},{"name":"Convert import","description":"Convert namespace import to named imports","actions":[{"name":"Convert namespace import to named imports","description":"Convert namespace import to named imports","kind":"refactor.rewrite.import.named","notApplicableReason":"Selection is not an import declaration."}]},{"name":"Convert import","description":"Convert named imports to namespace import","actions":[{"name":"Convert named imports to namespace import","description":"Convert named imports to namespace import","kind":"refactor.rewrite.import.namespace","notApplicableReason":"Selection is not an import declaration."}]},{"name":"Extract Symbol","description":"Extract function","actions":[{"name":"Extract Function","description":"Extract function","kind":"refactor.extract.function","notApplicableReason":"Cannot extract empty range."}]},{"name":"Extract Symbol","description":"Extract constant","actions":[{"name":"Extract Constant","description":"Extract constant","kind":"refactor.extract.constant","notApplicableReason":"Cannot extract empty range."}]},{"name":"Extract type","description":"Extract type","actions":[{"name":"Extract to typedef","description":"Extract to typedef","kind":"refactor.extract.typedef","notApplicableReason":"Selection is not a valid type node"},{"name":"Extract to type alias","description":"Extract to type alias","kind":"refactor.extract.type","notApplicableReason":"Selection is not a valid type node"},{"name":"Extract to interface","description":"Extract to interface","kind":"refactor.extract.interface","notApplicableReason":"Selection is not a valid type node"}]},{"name":"Generate 'get' and 'set' accessors","description":"Generate 'get' and 'set' accessors","actions":[{"name":"Generate 'get' and 'set' accessors","description":"Generate 'get' and 'set' accessors","kind":"refactor.rewrite.property.generateAccessors","notApplicableReason":"Could not find property for which to generate accessor"}]},{"name":"Move to a new file","description":"Move to a new file","actions":[{"name":"Move to a new file","description":"Move to a new file","kind":"refactor.move.newFile","notApplicableReason":"Selection is not a valid statement or statements"}]},{"name":"Add or remove braces in an arrow function","description":"Add or remove braces in an arrow function","actions":[{"name":"Add braces to arrow function","description":"Add braces to arrow function","kind":"refactor.rewrite.arrow.braces.add","notApplicableReason":"Could not find a containing arrow function"},{"name":"Remove braces from arrow function","description":"Remove braces from arrow function","kind":"refactor.rewrite.arrow.braces.remove","notApplicableReason":"Could not find a containing arrow function"}]},{"name":"Convert to template string","description":"Convert to template string","actions":[{"name":"Convert to template string","description":"Convert to template string","kind":"refactor.rewrite.string","notApplicableReason":"Can only convert string concatenation"}]},{"name":"Infer function return type","description":"Infer function return type","actions":[{"name":"Infer function return type","description":"Infer function return type","kind":"refactor.rewrite.function.returnType","notApplicableReason":"Return type must be inferred from a function"}]}]}
Info 72   [16:28:04.911] request:
    {"seq":8,"type":"request","command":"geterr","arguments":{"delay":0,"files":["c:/Users/JJH/Developer/node-sandbox/BaseModel.ts","c:/Users/JJH/Developer/node-sandbox/ExtendedQueryBuilder.ts","c:/Users/JJH/Developer/node-sandbox/app.ts","c:/Users/JJH/Developer/node-sandbox/person.model.ts"]}}
Perf 73   [16:28:04.912] 8::geterr: async elapsed time (in milliseconds) 0.6479
Info 74   [16:28:04.913] event:
    {"seq":0,"type":"event","event":"syntaxDiag","body":{"file":"c:/Users/JJH/Developer/node-sandbox/BaseModel.ts","diagnostics":[]}}
Err 75    [16:28:05.852] Exception on executing command delayed processing of request 8:

    Maximum call stack size exceeded

    RangeError: Maximum call stack size exceeded
        at compareSignaturesRelated (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:62465:42)
        at signatureRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64812:24)
        at signaturesRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64746:39)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64202:39)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at recursiveTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63681:64)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63197:34)
        at isPropertySymbolTypeRelated (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64436:24)
        at propertyRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64469:31)
        at propertiesRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64670:43)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64200:34)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at recursiveTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63681:64)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63197:34)
        at isPropertySymbolTypeRelated (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64436:24)
        at propertyRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64469:31)
        at propertiesRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64670:43)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63720:28)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at recursiveTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63681:64)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63243:31)
        at typeRelatedToSomeType (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63460:35)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63732:32)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63191:30)
        at checkTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:62809:26)
        at isTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:62760:24)
        at isTypeAssignableTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:61966:20)
        at getConditionalType (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:60775:77)
        at getConditionalTypeInstantiation (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:61748:25)
        at getConstraintOfDistributiveConditionalType (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:57146:40)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64121:54)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at recursiveTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63681:64)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63197:34)
        at eachTypeRelatedToType (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63527:35)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63729:29)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63191:30)
        at isRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63095:24)
        at compareSignaturesRelated (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:62578:25)
        at signatureRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64812:24)
        at signaturesRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64746:39)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64202:39)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at recursiveTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63681:64)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63197:34)
        at isPropertySymbolTypeRelated (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64436:24)
        at propertyRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64469:31)
        at propertiesRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64670:43)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64200:34)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at recursiveTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63681:64)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63197:34)
        at isRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63095:24)
        at compareSignaturesRelated (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:62578:25)
        at signatureRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64812:24)
        at signaturesRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64746:39)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64202:39)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at recursiveTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63681:64)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63197:34)
        at isPropertySymbolTypeRelated (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64436:24)
        at propertyRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64469:31)
        at propertiesRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64670:43)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64200:34)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at recursiveTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63681:64)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63197:34)
        at isPropertySymbolTypeRelated (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64436:24)
        at propertyRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64469:31)
        at propertiesRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64670:43)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63720:28)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at recursiveTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63681:64)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63243:31)
        at typeRelatedToSomeType (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63460:35)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63732:32)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63191:30)
        at checkTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:62809:26)
        at isTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:62760:24)
        at isTypeAssignableTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:61966:20)
        at getConditionalType (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:60775:77)
        at getConditionalTypeInstantiation (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:61748:25)
        at getConstraintOfDistributiveConditionalType (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:57146:40)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64121:54)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at recursiveTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63681:64)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63197:34)
        at eachTypeRelatedToType (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63527:35)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63729:29)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
        at isRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63191:30)
        at isRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63095:24)
        at compareSignaturesRelated (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:62578:25)
        at signatureRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64812:24)
        at signaturesRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64746:39)
        at structuredTypeRelatedToWorker (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:64202:39)
        at structuredTypeRelatedTo (c:\Users\JJH\Developer\node-sandbox\node_modules\typescript\lib\tsserver.js:63714:30)
Info 76   [16:28:05.852] event:
    {"seq":0,"type":"event","event":"requestCompleted","body":{"request_seq":8}}
@JeongJuhyeon
Copy link
Author

JeongJuhyeon commented Dec 20, 2021

Splitting up the extension does let inference succeed on 4.5, but it's incredibly slow to a point where it's unusable. On 4.6-dev it immediately gives up and goes with ''any".

Code example:

import { Model, Page, QueryBuilder } from 'objection';

class QueryBuilderExtension<M extends Model, R = M[]> extends QueryBuilder<
  M,
  R
> {
  ifWhere(predicate: any): this {
    if (!predicate) {
      return this;
    } else {
      return this.where('a', 1);
    }
  }
}

export class ExtendedQueryBuilder<
  M extends Model,
  R = M[]
> extends QueryBuilder<M, R> {
  // These are necessary. You can just copy-paste them and change the name of the query builder class.
  ArrayQueryBuilderType!: QueryBuilderExtension<M, M[]>;
  SingleQueryBuilderType!: QueryBuilderExtension<M, M>;
  MaybeSingleQueryBuilderType!: QueryBuilderExtension<M, M | undefined>;
  NumberQueryBuilderType!: QueryBuilderExtension<M, number>;
  PageQueryBuilderType!: QueryBuilderExtension<M, Page<M>>;
}

export default class Person extends Model {
  id!: number;

  QueryBuilderType!: ExtendedQueryBuilder<this>;
  static QueryBuilder = ExtendedQueryBuilder;

  static get tableName() {
    return 'map';
  }
}

async function getPerson() {
  const k = await Person.query();
}
Info 84   [17:01:59.141] request:
    {"seq":22,"type":"request","command":"encodedSemanticClassifications-full","arguments":{"file":"c:/Users/JJH/Developer/node-sandbox/BaseModel.ts","start":0,"length":1101,"format":"2020"}}
Perf 85   [17:02:06.972] 22::encodedSemanticClassifications-full: elapsed time (in milliseconds) 7830.9148
Info 86   [17:02:06.972] response:
    {"seq":0,"type":"response","command":"encodedSemanticClassifications-full","request_seq":22,"success":true,"body":{"spans":[64,21,257,86,1,1281,96,5,256,103,1,1281,107,1,1280,120,12,256,137,1,1280,143,1,1280,153,7,3073,161,9,1793,195,9,1792,261,5,3072,307,20,257,332,1,1281,342,5,256,352,1,1281,356,1,1280,371,12,256,384,1,1280,387,1,1280,499,21,2561,523,21,256,545,1,1280,548,1,1280,557,22,2561,582,21,256,604,1,1280,607,1,1280,614,27,2561,644,21,256,666,1,1280,669,1,1280,688,22,2561,713,21,256,735,1,1280,750,20,2561,773,21,256,795,1,1280,798,4,768,803,1,1280,835,6,257,850,5,256,861,2,2561,879,16,2561,898,20,256,936,12,259,951,20,256,989,9,2563,1048,9,2821,1071,1,2089,1081,6,256,1088,5,3074],"endOfLineState":0}}

That's 7.8 seconds until it was able to show me the type of "k".

@koskimas
Copy link
Collaborator

koskimas commented Dec 20, 2021

You need to run typescript in strict mode. Something in typescript ~4.4 changed that broke this. This isn't a bug in objection. Objection's typings are valid typescript. They just happen to hit a corner case bug in typescript compiler itself. I don't know if it's possible to fix this for the non-strict mode.

@JeongJuhyeon
Copy link
Author

JeongJuhyeon commented Dec 21, 2021

You need to run typescript in strict mode. Something in typescript ~4.4 changed that broke this. This isn't a bug in objection. Objection's typings are valid typescript. They just happen to hit a corner case bug in typescript compiler itself. I don't know if it's possible to fix this for the non-strict mode.

@koskimas I am (and always have been) running in strict mode; the tsconfig used in my example is the default. It also works in 4.4, albeit performance is a bit slow. I've checked all the TS versions from 4.4 and up and it (the minimal repro example) starts failing with version 4.5.

If this is going to take a long time to fix I would suggest at least adding a warning to the "Extending the query builder" part of the documentation that it may break inference for TS >4.5; this is now the default TS version and as I've shown it is guaranteed to happen when extending the QB using the method described in the current docs.

@JeongJuhyeon
Copy link
Author

JeongJuhyeon commented Dec 21, 2021

I've done more testing. Even with the very simplest model possible

export class User extends Model {
  static tableName = 'user';
  name!: string;
}

TS performance is very slow in TS 4.5 compared to TS 4.4. Just a simple const k = await User.query().andWhere('id', 5); and then hovering over "k" can take 3+ seconds of "Loading.." before "User[]" pops up. It looks like extending the QB just makes it so slow/need so many calls that tsserver gives up entirely, but extending the QB is not the underlying issue here.

@JeongJuhyeon JeongJuhyeon changed the title Extending the Query Builder breaks Typescript (4.5.4) Poor TS performance in TS 4.5 leading to errors Dec 21, 2021
@koskimas
Copy link
Collaborator

koskimas commented Dec 30, 2021

@Linksku may have found the fix here. Their fix is implemented in 3.0.1. Please try it out and report if it fixes your issues.

@JeongJuhyeon
Copy link
Author

JeongJuhyeon commented Jan 3, 2022

@Linksku may have found the fix here. Their fix is implemented in 3.0.1. Please try it out and report if it fixes your issues.

TS 4.4.4:
Changing my QB extension from a class to an interface as suggested in @Linksku's issue makes inference ~2x faster.
Changing the type of PartialModelObject/PartialModelGraph as in @Linksku's comment makes no difference.

TS 4.5.4:
Without both 1. changing the type of PartialModelObject/PartialModelGraph and 2. changing my QB extension from a class to an interface, inference completely gives up and breaks. I need to apply both of the changes for inference to work. After applying them, I get similar performance to TS 4.4.4.

@koskimas
Copy link
Collaborator

koskimas commented Jan 3, 2022

@JeongJuhyeon Your original repro in this issue is really snappy and never fails when I try it. The difference must be in our tsconfig files. Could you paste your's here?

@JeongJuhyeon
Copy link
Author

JeongJuhyeon commented Jan 6, 2022

@koskimas
It's the completely default tsconfig.

Steps

yarn init
yarn add objection typescript
npx tsc --init // Press enter on everything
code .
  1. Create app.ts and paste the content of my post.
  2. Hover over 'k'. Popup shows "k: any". Open the TS Server log. There's a "RangeError: Maximum call stack size exceeded"

tsconfig just for reference

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Projects */
    // "incremental": true,                              /* Enable incremental compilation */
    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
    // "tsBuildInfoFile": "./",                          /* Specify the folder for .tsbuildinfo incremental compilation files. */
    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects */
    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */

    /* Language and Environment */
    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
    // "jsx": "preserve",                                /* Specify what JSX code is generated. */
    // "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
    // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
    // "reactNamespace": "",                             /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */

    /* Modules */
    "module": "commonjs",                                /* Specify what module code is generated. */
    // "rootDir": "./",                                  /* Specify the root folder within your source files. */
    // "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */
    // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
    // "typeRoots": [],                                  /* Specify multiple folders that act like `./node_modules/@types`. */
    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
    // "resolveJsonModule": true,                        /* Enable importing .json files */
    // "noResolve": true,                                /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */

    /* JavaScript Support */
    // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
    // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */

    /* Emit */
    // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
    // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
    // "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
    // "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
    // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
    // "outDir": "./",                                   /* Specify an output folder for all emitted files. */
    // "removeComments": true,                           /* Disable emitting comments. */
    // "noEmit": true,                                   /* Disable emitting files from a compilation. */
    // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
    // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types */
    // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
    // "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
    // "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
    // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
    // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
    // "newLine": "crlf",                                /* Set the newline character for emitting files. */
    // "stripInternal": true,                            /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
    // "noEmitHelpers": true,                            /* Disable generating custom helper functions like `__extends` in compiled output. */
    // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
    // "preserveConstEnums": true,                       /* Disable erasing `const enum` declarations in generated code. */
    // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
    // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */

    /* Interop Constraints */
    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */

    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied `any` type.. */
    // "strictNullChecks": true,                         /* When type checking, take into account `null` and `undefined`. */
    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
    // "strictBindCallApply": true,                      /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
    // "noImplicitThis": true,                           /* Enable error reporting when `this` is given the type `any`. */
    // "useUnknownInCatchVariables": true,               /* Type catch clause variables as 'unknown' instead of 'any'. */
    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
    // "noUnusedLocals": true,                           /* Enable error reporting when a local variables aren't read. */
    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read */
    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
    // "noUncheckedIndexedAccess": true,                 /* Include 'undefined' in index signature results */
    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type */
    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */

    /* Completeness */
    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  }
}

If this doesn't happen for you all I can think of is an IDE difference.
VS Code: 1.63.2
Node.js: 14.16.0
Command Palette -> Open TS Server Log -> Enable logging
Command Palette -> Select Typescript Version: Use Workspace Version 4.5.4

@JeongJuhyeon
Copy link
Author

For reference, it's the same when running tsc.

➜  node-sandbox npx tsc
node-sandbox\node_modules\typescript\lib\tsc.js:94444
                throw e;

RangeError: Maximum call stack size exceeded

@jakebailey
Copy link

Can anyone reproduce this in more recent TS versions than 4.5.4? I'm trying to fix this in TS but #2178 (comment) implies that this is likely fixed (and I can't reproduce it at all).

@jakebailey
Copy link

I did a bisect using the info in this issue, and microsoft/TypeScript#47341 appears to have fixed this (which was also reported on microsoft/TypeScript#47142), so I believe this issue can be closed.

@lehni
Copy link
Collaborator

lehni commented Apr 14, 2023

I am unable to reproduce the problem on master with the provided example. Closing this.

@lehni lehni closed this as completed Apr 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants