Objection.js: Compilation breaks with TypeScript 3.9

Created on 13 May 2020  路  15Comments  路  Source: Vincit/objection.js

Upgraded a project from Typescript 3.8.2 to TypeScript 3.9.2.
The project is using the latest Objection (2.1.3).

After the TS upgrade, the project fails to compile with compilation errors related to Objection types.

I suspect this is related to "Stricter Checks on Intersections and Optional Properties" as described here.

```
node_modules/objection/typings/objection/index.d.ts:268:43 - error TS2344: Type 'Model & T' does not satisfy the constraint 'Model'.
The types of 'QueryBuilderType.findById(...).update(...).page(...).insertGraph' are incompatible between these types.
Type 'InsertGraphMethod' is not assignable to type 'InsertGraphMethod'.
Types of parameters 'graph' and 'graph' are incompatible.
Type 'PartialModelGraph' is not assignable to type 'PartialModelGraph'.

268 ? SingleQueryBuilder>
~

node_modules/objection/typings/objection/index.d.ts:271:26 - error TS2344: Type 'Model & I' does not satisfy the constraint 'Model'.
The types of 'QueryBuilderType.findById(...).update(...).page(...).insertGraph' are incompatible between these types.
Type 'InsertGraphMethod' is not assignable to type 'InsertGraphMethod'.
Types of parameters 'graph' and 'graph' are incompatible.
Type 'PartialModelGraph' is not assignable to type 'PartialModelGraph'.

271 ? QueryBuilderType
~

node_modules/objection/typings/objection/index.d.ts:281:24 - error TS2344: Type 'Model & T' does not satisfy the constraint 'Model'.
The types of 'QueryBuilderType.findById(...).update(...).page(...).insertGraph' are incompatible between these types.
Type 'InsertGraphMethod' is not assignable to type 'InsertGraphMethod'.
Types of parameters 'graph' and 'graph' are incompatible.
Type 'PartialModelGraph' is not assignable to type 'PartialModelGraph'.

281 ? QueryBuilderType
~

node_modules/objection/typings/objection/index.d.ts:284:26 - error TS2344: Type 'Model & I' does not satisfy the constraint 'Model'.
The types of 'QueryBuilderType.findById(...).update(...).page(...).insertGraph' are incompatible between these types.
Type 'InsertGraphMethod' is not assignable to type 'InsertGraphMethod'.
Types of parameters 'graph' and 'graph' are incompatible.
Type 'PartialModelGraph' is not assignable to type 'PartialModelGraph'.

284 ? QueryBuilderType```

help wanted typings

Most helpful comment

The fix is now released. No need for any workarounds.

All 15 comments

This is a pretty impossible situation. The thing that's breaking is actually a fix for this typescript bug. That bug has been fixed in the current typescript version, but if I roll back the objection fix that works around that bug, the typings will be broken for everyone still using typescript <= 3.8.

Somebody will suggest a major version bump, but that's not going to happen. Those are reserved for bigger changes.

The only thing we can do is to revert the fix to make typescript 3.9 work, and then we'll get ten of these issues about things being broken with typescript <= 3.8.

This is the fix in objection https://github.com/Vincit/objection.js/commit/46c310536f2f5d8462886dd885e2667f5a2e8629.

Actually, could somebody try to revert the above fix to see if the problem goes away? It fixed the problem in my small test, but I'd like to be sure it's the only thing broken.

Hello @koskimas

I updated to typescript 3.9.2 and had the above error.
If I revert the fix in objection's index.d.ts, I still have an error:

node_modules/objection/typings/objection/index.d.ts:268:43 - error TS2344: Type 'Model & T' does not satisfy the constraint 'Model'.
  The types of 'QueryBuilderType.findById(...).update(...).page(...).insertGraph' are incompatible between these types.
    Type 'InsertGraphMethod<Model & T>' is not assignable to type 'InsertGraphMethod<Model>'.
      Types of parameters 'graph' and 'graph' are incompatible.
        Type 'PartialModelGraph<Model>' is not assignable to type 'PartialModelGraph<Model & T>'.
          Type 'PartialModelGraph<Model>' is not assignable to type '{ [K in { [K in keyof (Model & T)]: (Model & T)[K] extends Function ? never : K; }["toJSON" | "$id" | "QueryBuilderType" | "$relatedQuery" | "$query" | ... 29 more ... | keyof T]]?: (Exclude<...> extends Model ? PartialModelGraph<...> : Exclude<...> extends (infer I)[] ? I extends Model ? PartialModelGraph<...>[] : ...'.

268     ? SingleQueryBuilder<QueryBuilderType<T>>
                                              ~

node_modules/objection/typings/objection/index.d.ts:271:26 - error TS2344: Type 'Model & I' does not satisfy the constraint 'Model'.
  The types of 'QueryBuilderType.findById(...).update(...).page(...).insertGraph' are incompatible between these types.
    Type 'InsertGraphMethod<Model & I>' is not assignable to type 'InsertGraphMethod<Model>'.
      Types of parameters 'graph' and 'graph' are incompatible.
        Type 'PartialModelGraph<Model>' is not assignable to type 'PartialModelGraph<Model & I>'.
          Type 'PartialModelGraph<Model>' is not assignable to type '{ [K in { [K in keyof (Model & I)]: (Model & I)[K] extends Function ? never : K; }["toJSON" | "$id" | "QueryBuilderType" | "$relatedQuery" | "$query" | ... 29 more ... | keyof I]]?: (Exclude<...> extends Model ? PartialModelGraph<...> : Exclude<...> extends (infer I)[] ? I extends Model ? PartialModelGraph<...>[] : ...'.

271       ? QueryBuilderType<I>
                             ~

node_modules/objection/typings/objection/index.d.ts:281:24 - error TS2344: Type 'Model & T' does not satisfy the constraint 'Model'.
  The types of 'QueryBuilderType.findById(...).update(...).page(...).insertGraph' are incompatible between these types.
    Type 'InsertGraphMethod<Model & T>' is not assignable to type 'InsertGraphMethod<Model>'.
      Types of parameters 'graph' and 'graph' are incompatible.
        Type 'PartialModelGraph<Model>' is not assignable to type 'PartialModelGraph<Model & T>'.
          Type 'PartialModelGraph<Model>' is not assignable to type '{ [K in { [K in keyof (Model & T)]: (Model & T)[K] extends Function ? never : K; }["toJSON" | "$id" | "QueryBuilderType" | "$relatedQuery" | "$query" | ... 29 more ... | keyof T]]?: (Exclude<...> extends Model ? PartialModelGraph<...> : Exclude<...> extends (infer I)[] ? I extends Model ? PartialModelGraph<...>[] : ...'.

281     ? QueryBuilderType<T>
                           ~

node_modules/objection/typings/objection/index.d.ts:284:26 - error TS2344: Type 'Model & I' does not satisfy the constraint 'Model'.
  The types of 'QueryBuilderType.findById(...).update(...).page(...).insertGraph' are incompatible between these types.
    Type 'InsertGraphMethod<Model & I>' is not assignable to type 'InsertGraphMethod<Model>'.
      Types of parameters 'graph' and 'graph' are incompatible.
        Type 'PartialModelGraph<Model>' is not assignable to type 'PartialModelGraph<Model & I>'.
          Type 'PartialModelGraph<Model>' is not assignable to type '{ [K in { [K in keyof (Model & I)]: (Model & I)[K] extends Function ? never : K; }["toJSON" | "$id" | "QueryBuilderType" | "$relatedQuery" | "$query" | ... 29 more ... | keyof I]]?: (Exclude<...> extends Model ? PartialModelGraph<...> : Exclude<...> extends (infer I)[] ? I extends Model ? PartialModelGraph<...>[] : ...'.

284       ? QueryBuilderType<I>
                             ~


Found 4 errors.

@francoisromain Thanks. I'm able to reproduce this now too.

I'm baffled. I have no idea how to fix this. There's not a single A & B type in objection types anywhere (after changing the GraphParameters thing) and I still get the error. I don't think I'll be able to solve this one. I really need help from people that are more skilled in typescript.

Haha, I may have found a solution by accident. Could you try the typescript-39-support branch to see if you get any problems? I'd especially like to know that we don't lose any type safety (things don't turn into any because of this).

@koskimas the modification in the typescript-39-support branch fixed the compilation with ts 3.9.2. Cool! I am not sure how to check this part:

I'd especially like to know that we don't lose any type safety (things don't turn into any because of this).

That's a bit tricky as the any type spreads like cancer. Even more so if you don't have the noExplicitAny config enabled. I went through the type tests (by checking the actual type of the expressions) and I couldn't find any problems. I think I'm going to go ahead and release the fix tomorrow. I need to add some real tests for the typings using something like dtslint.

dtslint is great, I use it a lot, but be warned that TSLint is deprecated.

You might be able to maintain backwards compatibility by detecting the TypeScript version and providing different types based on that.

Using the example from the TS 3.9 announcement: ([playground])

interface A {
    a: number; // notice this is 'number'
}

interface B {
    b: string;
}

interface C {
    a?: boolean; // notice this is 'boolean'
    b: string;
}

// this type will be different depending on the compiler version
type Result = A & B extends C ? '< 3.9' : '>= 3.9';

There's no backwards compatibility issues with the fix.

You can set:

"strict": false,
"strictFunctionTypes": false,
"alwaysStrict": false,

in your tsconfig.json as a temp workaround.

Disabling "strict" turns TS into a glorified linter.

A better workaround is to import the git branch with the fix. Or fork the repo at the last released version and cherry-pick the fix.

The fix is now released. No need for any workarounds.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mycahjay-nms picture mycahjay-nms  路  4Comments

apronin83 picture apronin83  路  3Comments

nazar picture nazar  路  3Comments

Gustav0ar picture Gustav0ar  路  4Comments

louis-etne picture louis-etne  路  4Comments