Skip to content

fix(server-core): correct return type of DialectFactoryFn and ExternalDialectFactoryFn - #11457

Open
AdvMaple wants to merge 2 commits into
cube-js:masterfrom
AdvMaple:fix/dialect-factory-fn-return-type
Open

fix(server-core): correct return type of DialectFactoryFn and ExternalDialectFactoryFn#11457
AdvMaple wants to merge 2 commits into
cube-js:masterfrom
AdvMaple:fix/dialect-factory-fn-return-type

Conversation

@AdvMaple

@AdvMaple AdvMaple commented Aug 3, 2026

Copy link
Copy Markdown

Problem

Writing a custom Cube.js dialect produces a TypeScript error with no clean fix.

image

DialectFactoryFn is typed to return a BaseQuery instance, but at runtime Cube.js calls the return value with new. So a correct factory like (ctx) => HanaQuery fails type-checking, and the only escape hatch is:

dialectFactory: (ctx) => HanaQuery as unknown as BaseQuery

That cast is wrong — and misleading to readers.


Root Cause

#1997 tightened the type from any to BaseQuery, which was good — but BaseQuery and typeof BaseQuery mean different things:

Type Meaning
BaseQuery An instance of the class
typeof BaseQuery The class itself — callable with new

The factory must return a constructor, not an instance.


Context

Discovered while vendoring a local SAP HANA driver (the original PR #5648 was never merged). dialectFactory: (ctx) => HanaQuery is the correct pattern — it matches every built-in dialect — but it triggers the error above.

BaseQuery has 295+ instance properties populated at construction time by the framework. A custom dialect author cannot satisfy that shape directly; extends BaseQuery is the right approach. The error is purely a type annotation bug.


Fix

// Before
export type DialectFactoryFn         = (context: DialectContext) => BaseQuery;
export type ExternalDialectFactoryFn = (context: RequestContext) => BaseQuery;

// After
export type DialectFactoryFn         = (context: DialectContext) => typeof BaseQuery;
export type ExternalDialectFactoryFn = (context: RequestContext) => typeof BaseQuery;

With this change, dialectFactory: (ctx) => MyCustomQuery type-checks naturally for any MyCustomQuery extends BaseQuery — no casts needed.


Testing

Type-only change. No runtime behaviour altered. TypeScript compilation of packages/cubejs-server-core passes.

…lDialectFactoryFn

Both DialectFactoryFn and ExternalDialectFactoryFn were typed as returning
BaseQuery (an instance), but the runtime uses the return value as a constructor:

  new (queryClass(dbType, dialectClass))(compilers, queryOptions)

The dialectFactory is expected to return a class (subclass of BaseQuery),
not an instance. PR cube-js#1997 improved the type from 'any' to 'BaseQuery' but
the correct type is 'typeof BaseQuery'.

Evidence:
- CompilerApi.getDialectClass() returns what dialectFactory returned and
  passes it directly to QueryBuilder.queryClass(), which calls new on it.
- The contributing guide (CONTRIBUTING.md) shows dialectClass() returning
  FooQuery (a class reference), not new FooQuery().
- BaseQuery JSDoc: 'You should never instantiate this class manually.'
- All built-in dialects (MysqlQuery, PostgresQuery, etc.) are registered
  as class constructors, not instances.

This change makes custom dialect registration type-safe without requiring
casts or ts-ignore suppression.
@AdvMaple
AdvMaple requested a review from a team as a code owner August 3, 2026 16:45
@github-actions github-actions Bot added javascript Pull requests that update Javascript code pr:community Contribution from Cube.js community members. labels Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

javascript Pull requests that update Javascript code pr:community Contribution from Cube.js community members.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant