Skip to content

fix(ui): HasSsrOrSsg defaults to SSR branch for non-SSR apps#18356

Open
maiolica wants to merge 1 commit into
quasarframework:devfrom
maiolica:fix/hasssrorssg-non-ssr
Open

fix(ui): HasSsrOrSsg defaults to SSR branch for non-SSR apps#18356
maiolica wants to merge 1 commit into
quasarframework:devfrom
maiolica:fix/hasssrorssg-non-ssr

Conversation

@maiolica

@maiolica maiolica commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Documentation
  • Code style update
  • Refactor
  • Build-related changes
  • Other, please describe:

Does this PR introduce a breaking change?

  • Yes
  • No

The PR fulfills these requirements:

  • It's submitted to the dev branch (or v[X] branch)
  • When resolving a specific issue, it's referenced in the PR's title (no filed issue; happy to open one and reference it if preferred)
  • It's been tested on a Cordova (iOS, Android) app (N/A: type-definition-only change, no runtime code affected)
  • It's been tested on an Electron app (N/A: type-definition-only change, no runtime code affected)
  • Any necessary documentation has been added or updated, or explained in the PR's description (explained below; no docs change needed)

Other information:

Problem

In a non-SSR/SSG app (SPA, PWA, Electron, Capacitor, Cordova, BEX), the programmatic language / icon-set setters no longer type-check when called with a single argument:

import { Quasar } from 'quasar';
Quasar.lang.set(quasarLanguagePack); // TS2554: Expected 2 arguments, but got 1.
Quasar.iconSet.set(iconSet);         // same

This worked through 2.21.1 and regressed in 2.21.2 (commit adc5d54, "feat(ui): prepare types for upcoming SSG mode", which switched the lang/iconSet singleton set from HasSsr<...> to HasSsrOrSsg<...>). Still present on dev.

Root cause

Quasar.lang / Quasar.iconSet are the *Singleton interfaces whose set overload is chosen by HasSsrOrSsg (ui/types/globals.d.ts):

HasSsrOrSsg<
  { set(lang: QuasarLanguage, ssrContext: any): void }, // SSR/SSG: 2 args
  { set(lang: QuasarLanguage): void }                   // non-SSR: 1 arg
>

HasSsrOrSsg was defined with the opposite direction of every other feature-flag helper:

export type IsFeatureEnabled<O extends string, T, U = {}> =
  QuasarFeatureFlags[O] extends true ? T : U;               // absent flag -> U (off)
export type HasSsrOrSsg<T, U = {}> =
  true extends QuasarFeatureFlags["ssr" | "ssg"] ? T : U;   // absent flag -> T (on)  <-- bug

QuasarFeatureFlags has [index: string]: boolean, and the app CLIs augment it with only the enabled flags (e.g. store: true) - they never declare ssr: false. So in a non-SSR app QuasarFeatureFlags["ssr" | "ssg"] falls back to the index signature boolean, and true extends boolean is true, selecting the 2-argument SSR overload for everyone.

Fix

Check each flag against true (matching IsFeatureEnabled), so an absent flag defaults to U (off). QuasarFeatureFlags stays referenced; only the direction of the check changes. Equivalent one-liner: HasSsr<T, HasSsg<T, U>>.

Verification

Resolved a TS program across all flag combinations:

flags declared before (buggy) after (fix)
none (SPA) T (2-arg) U (1-arg)
ssr: true T T
ssg: true T T
ssr + ssg T T

Types-only, no runtime change; SSR/SSG apps keep the 2-arg overload. HasSsrOrSsg is used only by the lang and iconSet singleton set methods.

@github-actions

Copy link
Copy Markdown

Build Results

JSON API

📜 No changes detected.

Types

📜 Changes detected:

diff --git a/./current-build/types/feature-flag.d.ts b/./pr-build/types/feature-flag.d.ts
index 284c0dd..8de7cd7 100644
--- a/./current-build/types/feature-flag.d.ts
+++ b/./pr-build/types/feature-flag.d.ts
@@ -51,11 +51,11 @@ export type HasStore<T, U = {}> = IsFeatureEnabled<"store", T, U>;
 
 export type HasSsr<T, U = {}> = IsFeatureEnabled<"ssr", T, U>;
 export type HasSsg<T, U = {}> = IsFeatureEnabled<"ssg", T, U>;
-export type HasSsrOrSsg<T, U = {}> = true extends QuasarFeatureFlags[
-  | "ssr"
-  | "ssg"]
+export type HasSsrOrSsg<T, U = {}> = QuasarFeatureFlags["ssr"] extends true
   ? T
-  : U;
+  : QuasarFeatureFlags["ssg"] extends true
+    ? T
+    : U;
 
 export type HasPwa<T, U = {}> = IsFeatureEnabled<"pwa", T, U>;
 export type HasCapacitor<T, U = {}> = IsFeatureEnabled<"capacitor", T, U>;

@maiolica maiolica marked this pull request as ready for review July 10, 2026 12:24
@maiolica maiolica changed the title fix(ui): HasSsrOrSsg defaults to SSR branch for non-SSR apps fix(ui): HasSsrOrSsg defaults to SSR branch for non-SSR apps Jul 12, 2026
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

Successfully merging this pull request may close these issues.

1 participant