reduxjs / redux-toolkit

The official, opinionated, batteries-included toolset for efficient Redux development
https://redux-toolkit.js.org
MIT License
10.64k stars 1.15k forks source link

RTK generateEndpoints throws typescript error: Trace: TypeError: Cannot read property 'pos' of undefined #2425

Open isidornygren opened 2 years ago

isidornygren commented 2 years ago

Running the RTK code generation as described in the docs by running npx @rtk-query/codegen-openapi openapi-config.ts works fine without any issues.

However, running exactly the same config using the generateEndpoints function produces what looks like a typescript error. I've tried running it both using Node and esbuild-runner but it still errors. I've tried using different openapi endpoints but I still receive the same error when trying to run the function programmatically.

Setup

// src/store/emptyApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const emptySplitApi = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: () => ({}),
});
// apiConfig.ts
import type { ConfigFile } from '@rtk-query/codegen-openapi';

const apiConfig: ConfigFile = {
  // Using an example openapi endpoint found online
  schemaFile: 'https://petstore.swagger.io/v2/swagger.json',
  apiFile: './src/store/emptyApi.ts',
  apiImport: 'emptySplitApi',
  outputFile: './src/model/generated/api.ts',
  exportName: 'api',
  hooks: true,
};

export default apiConfig;
// generateApi.ts (run through node or esbuild-runner)
const config = {
  schemaFile: 'https://petstore.swagger.io/v2/swagger.json',
  apiFile: './src/store/emptyApi.ts',
  apiImport: 'emptySplitApi',
  outputFile: './src/model/generated/api.ts',
  exportName: 'api',
  hooks: true,
};

generateEndpoints(config).catch(console.trace);

Output

Trace: TypeError: Cannot read property 'pos' of undefined
    at emitNodeList (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:111266:48)
    at emitList (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:111158:13)
    at emitObjectLiteralExpression (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:109538:13)
    at pipelineEmitWithHintWorker (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108863:32)
    at pipelineEmitWithHint (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108479:17)
    at pipelineEmitWithComments (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:112033:13)
    at pipelineEmit (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108419:13)
    at emitExpression (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108403:13)
    at emitNodeList (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:111271:25)
    at emitExpressionList (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:111161:13)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
phryneas commented 2 years ago

Have you tried calling parseConfig before?

See https://github.com/reduxjs/redux-toolkit/blob/a5cf61b366f23a868548d1ca7d56350c76610cbf/packages/rtk-query-codegen-openapi/src/bin/cli.ts#L56-L71

isidornygren commented 2 years ago

Have you tried calling parseConfig before?

See

https://github.com/reduxjs/redux-toolkit/blob/a5cf61b366f23a868548d1ca7d56350c76610cbf/packages/rtk-query-codegen-openapi/src/bin/cli.ts#L56-L71

Yes, I've tried it and it gave me what looked like the same error. However, when double checking i saw that it was slightly different (Albeit still seems to be a typescript parsing error, and I still have no idea why it's happening):

Trace: TypeError: Cannot read property 'end' of undefined
    at emitTupleType (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:109373:76)
    at pipelineEmitWithHintWorker (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108569:32)
    at pipelineEmitWithHint (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108479:17)
    at pipelineEmitWithComments (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:112033:13)
    at pipelineEmit (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108419:13)
    at emit (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108392:13)
    at emitNodeList (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:111271:25)
    at emitList (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:111158:13)
    at emitUnionType (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:109388:13)
    at pipelineEmitWithHintWorker (/Users/***/***/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108574:32)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)

Using this code:

const config = parseConfig({
  schemaFile: 'https://petstore.swagger.io/v2/swagger.json',
  apiFile: './src/store/emptyApi.ts',
  apiImport: 'emptySplitApi',
  outputFile: './src/model/generated/api.ts',
  exportName: 'api',
  hooks: true
});

config.forEach((c) => generateEndpoints(c).catch(console.trace));
kdevan commented 2 years ago

I'm running into the same issue:

TypeError: Cannot read properties of undefined (reading 'end')
    at emitTupleType (/app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:109480:76)
    at pipelineEmitWithHintWorker (/app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108676:32)
    at pipelineEmitWithHint (/app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108586:17)
    at pipelineEmitWithComments (/app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:112140:13)
    at pipelineEmit (/app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108526:13)
    at emit (/app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108499:13)
    at emitNodeList (/app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:111378:25)
    at emitList (/app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:111265:13)
    at emitUnionType (/app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:109495:13)
    at pipelineEmitWithHintWorker (/app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108681:32)

Also tried adding parseConfig but still getting the same error. I did have this working in a previous version, running into this after updating react, @reduxjs/toolkit, react-redux, and @rtk-query/codegen-openapi to latest versions.

Edit: I wouldn't mind digging into this. Is there some way to get a trace that leads me in the right direction? I'm really just not sure where I would start since the trace is all typescript library file.

phryneas commented 2 years ago

I just tried this and for me it is working. Maybe it is your TypeScript version? I just tried with 4.5.2.

kdevan commented 2 years ago

Ah yeah, didn't think of that. Currently, Typescript 4.7.4. I'll test with 4.5.2 and update here.

jkirkpatrick24 commented 2 years ago

I bumped up against this too. Using typescript 4.7.4 throws the error described above, but reverting to typescript v4.5.2 fixes it.

phryneas commented 2 years ago

Could you take a look what changed between those versions and if you could come up with a fix?

kdevan commented 2 years ago

The function that throws the error seems mostly the same between these versions:

https://raw.githubusercontent.com/microsoft/TypeScript/v4.5.2/lib/typescript.js

function emitTupleType(node) {
    emitTokenWithComment(22 /* OpenBracketToken */, node.pos, writePunctuation, node);
    var flags = ts.getEmitFlags(node) & 1 /* SingleLine */ ? 528 /* SingleLineTupleTypeElements */ : 657 /* MultiLineTupleTypeElements */;
    emitList(node, node.elements, flags | 524288 /* NoSpaceIfEmpty */);
    emitTokenWithComment(23 /* CloseBracketToken */, node.elements.end, writePunctuation, node);
}

https://raw.githubusercontent.com/microsoft/TypeScript/v4.7.4/lib/typescript.js

function emitTupleType(node) {
    emitTokenWithComment(22 /* SyntaxKind.OpenBracketToken */, node.pos, writePunctuation, node);
    var flags = ts.getEmitFlags(node) & 1 /* EmitFlags.SingleLine */ ? 528 /* ListFormat.SingleLineTupleTypeElements */ : 657 /* ListFormat.MultiLineTupleTypeElements */;
    emitList(node, node.elements, flags | 524288 /* ListFormat.NoSpaceIfEmpty */, parenthesizer.parenthesizeElementTypeOfTupleType);
    emitTokenWithComment(23 /* SyntaxKind.CloseBracketToken */, node.elements.end, writePunctuation, node);
}

Can see that node.elements.end is the offending object/array. I'll keep digging and update if I find anything.

Edit: Looks to me like filterEndpoints and endpointOverrides are the two possible properties that can be undefined and also use a possible List/NodeList and Tuple type? The stack trace mentions a UnionType -> List -> NodeList -> Tuple. I need to fix some stuff in my project and then I can test this theory out, I'll update as soon as I can.

kdevan commented 2 years ago

filterEndpoints and endpointOverrides ended up being the culprits. If you add them to the configuration with an empty array as the value then it works. Both parameters trigger the error.

{
    ...
    filterEndpoints: [],
    endpointOverrides: [],
}

Edit: False positive because I was just filtering every endpoint. I added regex to include everything so that I could pass a value to filterEndpoints without it being undefined but I still get the error. Still looking.

isidornygren commented 2 years ago

Reverting to typescript version 4.5.2 unfortunately does not seem to change anything for me, I've tried forcing the rtk-query codegen typescript version by adding:

{
  "devDependencies": {
     ...
     "typescript": "=4.5.2"
  },
  "resolutions": {
    "@rtk-query/codegen-openapi/typescript": "=4.5.2"
  },
}

But still getting the same issue:

Trace: TypeError: Cannot read property 'end' of undefined
    at emitTupleType (/Users/***/***/node_modules/typescript/lib/typescript.js:109342:76)
    at pipelineEmitWithHintWorker (/Users/***/***/node_modules/typescript/lib/typescript.js:108538:32)
    at pipelineEmitWithHint (/Users/***/***/node_modules/typescript/lib/typescript.js:108448:17)
    at pipelineEmitWithComments (/Users/***/***/node_modules/typescript/lib/typescript.js:112002:13)
    at pipelineEmit (/Users/***/***/node_modules/typescript/lib/typescript.js:108388:13)
    at emit (/Users/***/***/node_modules/typescript/lib/typescript.js:108361:13)
    at emitNodeList (/Users/***/***/node_modules/typescript/lib/typescript.js:111240:25)
    at emitList (/Users/***/***/node_modules/typescript/lib/typescript.js:111127:13)
    at emitUnionType (/Users/***/***/node_modules/typescript/lib/typescript.js:109357:13)
    at pipelineEmitWithHintWorker (/Users/***/***/node_modules/typescript/lib/typescript.js:108543:32)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)

Running yarn list --pattern typescript

yarn list v1.22.15
*omitted*
└─ typescript@4.5.2
✨  Done in 0.78s.
lindapaiste commented 2 years ago

I am able to reproduce this error in TS version 4.7.4 (but not on 4.3.5). I am running Node.js version 16.13.0 on Windows.

I'm hoping that I'll get a chance to dig into it and figure out how we can make the codegen compatible with the latest TS version.

I'm not sure at this point if the issue would occur if both the app and the codegen are using v4.7.4. When I saw the error it was with the app on the latest 4.7.4 but the codegen will load its own version fitting the specified range "typescript": ">=4.1 <=4.5".

This is the node which triggers the error when passed to emitTupleType. There is no elements property, so the node.elements.end gives that "TypeError: Cannot read property 'end' of undefined" error.

{
  "pos": -1,
  "end": -1,
  "flags": 8,
  "modifierFlagsCache": 0,
  "transformFlags": 1,
  "kind": 183,
  "elementType": {
    "pos": -1,
    "end": -1,
    "flags": 8,
    "modifierFlagsCache": 0,
    "transformFlags": 1,
    "kind": 178,
    "typeName": {
      "pos": -1,
      "end": -1,
      "flags": 8,
      "modifierFlagsCache": 0,
      "transformFlags": 0,
      "kind": 79,
      "escapedText": "Pet"
    }
  },
  "emitNode": {
    "leadingComments": [
      {
        "kind": 3,
        "pos": -1,
        "end": -1,
        "hasTrailingNewLine": false,
        "text": "* status 200 successful operation "
      }
    ]
  }
}

So we need to go up a few levels to figure out why emitTupleType is getting called with this type which is not a tuple.

The line that it is trying to write is:

export type FindPetsByStatusApiResponse = /** status 200 successful operation */ Pet[];

It should have an array of Pet, but somehow it winds up with type 183 (TupleType) instead. ArrayType is 182, so it's an off-by-one. I ask myself "would they ever change those numbers?"...and the answer is YES.

I am not yet fully understanding which parts of the code are executed with the app TS version and which use the codegen's version, but it seems like this is a version mismatch problem.

In my project's node_modules\typescript\lib\typescript.js, version 4.7.4, line 111166-111169:

case 183 /* SyntaxKind.ArrayType */:
  return emitArrayType(node);
case 184 /* SyntaxKind.TupleType */:
   return emitTupleType(node);

In the file where the error is triggered, node_modules\@rtk-query\codegen-openapi\node_modules\typescript\lib\typescript.js, version 4.5.5, line 108566-108569:

case 182 /* ArrayType */:
  return emitArrayType(node);
case 183 /* TupleType */:
  return emitTupleType(node);
phryneas commented 2 years ago

Oh, good catch. TS versions make a lot of sense. oazapfts has to run with the exact same TS version as the module itself - we are trying to enforce that with enforceOazapftsTsVersion but it seems that does not work here. Are y'all using some kind of special bundler? I could imagine this happening with yarn-pnp.

https://github.com/reduxjs/redux-toolkit/blob/b3d4d89b03dca0699f5bde05883e5775b26ce9ee/packages/rtk-query-codegen-openapi/src/index.ts#L48-L61

kdevan commented 2 years ago

Yeah, in my project oazapfts itself is using typescript 4.7.4 while rtk-query-codegen-openapi is using 4.5.5. Since oazapfts is used for typescript generation, nodes are being generated using 4.7.4 and then being processed by this library using 4.5.5.

In my case this is due to how I have yarn set up, using yarn 3.2.1 workspaces with nodeLinker: node-modules in .yarnrc.yml.

I tried adding a resolution to force everything to use 4.7.4 and everything is working so far:

"resolutions": {
  "typescript": "4.7.4"
}

Obviously this is risky to force libraries to use a version they don't expect but for now it's allowing development on this front to continue. :)