From ac807c317aeb825cc03ca853d8da76d9506ff8db Mon Sep 17 00:00:00 2001 From: Arya Emami Date: Wed, 25 Feb 2026 14:34:38 -0600 Subject: [PATCH] fix(codegen): narrow `generateEndpoints` return type Co-authored-by: Issy Szemeti <48881813+issy@users.noreply.github.com> --- .../rtk-query-codegen-openapi/src/index.ts | 8 +++++ .../test/generateEndpoints.test-d.ts | 31 ++++++++++++++++ .../test/generateEndpoints.test.ts | 23 ++++++++++++ .../vitest.config.mts | 35 ++++++++++++++----- 4 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 packages/rtk-query-codegen-openapi/test/generateEndpoints.test-d.ts diff --git a/packages/rtk-query-codegen-openapi/src/index.ts b/packages/rtk-query-codegen-openapi/src/index.ts index 41a251d63d..7b68762719 100644 --- a/packages/rtk-query-codegen-openapi/src/index.ts +++ b/packages/rtk-query-codegen-openapi/src/index.ts @@ -8,6 +8,14 @@ export type { ConfigFile } from './types'; const require = createRequire(__filename); +export async function generateEndpoints( + options: Omit & { outputFile?: never } +): Promise; + +export async function generateEndpoints( + options: Omit & Required> +): Promise; + export async function generateEndpoints(options: GenerationOptions): Promise { const schemaLocation = options.schemaFile; diff --git a/packages/rtk-query-codegen-openapi/test/generateEndpoints.test-d.ts b/packages/rtk-query-codegen-openapi/test/generateEndpoints.test-d.ts new file mode 100644 index 0000000000..2d4ba28bc4 --- /dev/null +++ b/packages/rtk-query-codegen-openapi/test/generateEndpoints.test-d.ts @@ -0,0 +1,31 @@ +import { generateEndpoints } from '@rtk-query/codegen-openapi'; +import * as path from 'node:path'; + +const withoutOutputFile = { + apiFile: './fixtures/emptyApi.ts', + schemaFile: path.join(__dirname, 'fixtures', 'petstore.json'), +}; + +const withOutputFile = { + apiFile: './fixtures/emptyApi.ts', + outputFile: './test/tmp/out.ts', + schemaFile: path.join(__dirname, 'fixtures', 'petstore.json'), +}; + +describe('generateEndpoints return type narrowing', () => { + test('narrows to Promise when outputFile is omitted', () => { + expectTypeOf(generateEndpoints(withoutOutputFile)).toEqualTypeOf>(); + + expectTypeOf(generateEndpoints(withoutOutputFile)).resolves.toBeString(); + + expectTypeOf(generateEndpoints).toBeCallableWith(withoutOutputFile).returns.resolves.toBeString(); + }); + + test('narrows to Promise when outputFile is provided', () => { + expectTypeOf(generateEndpoints(withOutputFile)).toEqualTypeOf>(); + + expectTypeOf(generateEndpoints(withOutputFile)).resolves.toBeVoid(); + + expectTypeOf(generateEndpoints).toBeCallableWith(withOutputFile).returns.resolves.toBeVoid(); + }); +}); diff --git a/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts b/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts index 20c895219d..c452a4253b 100644 --- a/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts +++ b/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts @@ -933,3 +933,26 @@ describe('esmExtensions option', () => { expect(content).toContain("import { api } from '../../fixtures/emptyApi'"); }); }); + +describe('generateEndpoints return type narrowing', () => { + const schemaFile = resolve(__dirname, 'fixtures', 'petstore.json'); + + test('returns a string when outputFile is omitted', async () => { + const result = await generateEndpoints({ + apiFile: './fixtures/emptyApi.ts', + schemaFile, + }); + + expect(result).toBeTypeOf('string'); + }); + + test('returns void when outputFile is provided', async () => { + const result = await generateEndpoints({ + apiFile: './fixtures/emptyApi.ts', + outputFile: './test/tmp/out.ts', + schemaFile, + }); + + expect(result).toBeUndefined(); + }); +}); diff --git a/packages/rtk-query-codegen-openapi/vitest.config.mts b/packages/rtk-query-codegen-openapi/vitest.config.mts index de73ff27d2..d411d934b9 100644 --- a/packages/rtk-query-codegen-openapi/vitest.config.mts +++ b/packages/rtk-query-codegen-openapi/vitest.config.mts @@ -1,23 +1,42 @@ -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; +import * as path from 'node:path'; import tsconfigPaths from 'vite-tsconfig-paths'; import { defineConfig } from 'vitest/config'; - -// No __dirname under Node ESM -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +import packageJson from './package.json' with { type: 'json' }; export default defineConfig({ - plugins: [tsconfigPaths({ projects: ['./tsconfig.json'] })], + plugins: [ + tsconfigPaths({ + configNames: ['tsconfig.json'], + projects: ['./tsconfig.json'], + root: import.meta.dirname, + }), + ], + + root: import.meta.dirname, + test: { + name: { + label: packageJson.name, + }, + alias: process.env.TEST_DIST ? { - '@rtk-query/codegen-openapi': path.join(__dirname, '../..', 'node_modules/@rtk-query/codegen-openapi'), + '@rtk-query/codegen-openapi': path.join(import.meta.dirname, '..', '..', 'node_modules', packageJson.name), } : undefined, + + dir: path.join(import.meta.dirname, 'test'), + root: import.meta.dirname, testTimeout: 10_000, + + typecheck: { + enabled: true, + tsconfig: path.join(import.meta.dirname, 'tsconfig.json'), + }, + pool: 'forks', globals: true, setupFiles: ['./test/vitest.setup.ts'], + watch: false, }, });