Skip to content

Restrict content scope values to string | number | null | undefined - #6295

Draft
VPS-Obi wants to merge 4 commits into
nextfrom
content-scope-value-types
Draft

Restrict content scope values to string | number | null | undefined#6295
VPS-Obi wants to merge 4 commits into
nextfrom
content-scope-value-types

Conversation

@VPS-Obi

@VPS-Obi VPS-Obi commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

This change narrows the type of content scope values across multiple packages from any to string | number | null | undefined. This prevents mistakes such as passing a scope class instead of a scope instance, or wrapping a scope in another object, by making the type system more restrictive.

Key Changes

  • Type Interface Updates:

    • ContentScope in @dextinity/cms-admin: Changed from [key: string]: any to [key: string]: string | number | null | undefined
    • ScopeInterface in @dextinity/cms-api (page tree): Updated to restrict values
    • RedirectScopeInterface in @dextinity/cms-api: Updated to restrict values
    • DamScopeInterface in @dextinity/cms-api: Updated to restrict values
    • EmailCampaignScopeInterface in @dextinity/brevo-api: Updated to restrict values
    • scope in preview params (@dextinity/site-nextjs): Updated to restrict values
  • Type Corrections in Resolvers and Services:

    • Changed resolver argument types from typeof Scope (class type) to EmailCampaignScopeInterface, DamScopeInterface, RedirectScopeInterface, and ScopeInterface (instance types)
    • Updated console command scope parsing to use interface types instead of class types
  • Internal Type Simplifications:

    • Removed NonNull<T> and NonNullRecord<T> utility types in favor of simpler ContentScopeRouterParams type
    • Updated scope formatting logic to handle null, undefined, and empty string values consistently
  • Demo Application Updates:

    • Added [key: string]: string index signature to all scope classes (PageTreeNodeScope, DamScope, EmailCampaignContentScope, NewsContentScope, RedirectScope, EmailContactSubscribeScope)
  • Component and Hook Updates:

    • Updated all components and hooks using scopes to properly type scope variables as ContentScope
    • Fixed string conversion in scope value comparisons and display logic

Migration Notes

Scope classes in applications need to declare an index signature. For scopes with only string dimensions, use [key: string]: string:

@Embeddable()
@ObjectType("PageTreeNodeScope")
@InputType("PageTreeNodeScopeInput")
export class PageTreeNodeScope {
    [key: string]: string;

    @Property({ columnType: "text" })
    @Field()
    @IsString()
    domain: string;

    @Property({ columnType: "text" })
    @Field()
    @IsString()
    language: string;
}

https://claude.ai/code/session_01NnYJ1LdUKw9FxdKdFAdWAr

The content scope interfaces accepted values of any type (Record<string, any>,
[key: string]: unknown), so mistakes involving scopes went unnoticed by the
compiler. Narrowing the value type to string | number | null | undefined
surfaced several of them:

- Embeddable scope properties and resolver scope arguments were typed
  `typeof Scope`, the scope class, instead of a scope instance.
- The Brevo redirect entity factory had the same problem.
- DeleteUnsubscribedBrevoContactsConsole wrapped the scope in another object
  before passing it to the Brevo contacts API, so the API client was resolved
  for a scope that can never match a Brevo config.

Router match params in the admin are always strings, so they no longer derive
their type from ContentScope.

Scope classes need an explicit index signature to stay assignable to the scope
interfaces, as TypeScript does not infer one for classes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnYJ1LdUKw9FxdKdFAdWAr
All scope dimensions in Demo are strings, and an index signature only has to
be assignable to the scope interface's, not identical to it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnYJ1LdUKw9FxdKdFAdWAr
A project's scope class declares its own index signature, so a scope with only
string dimensions keeps string values and needs no conversion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnYJ1LdUKw9FxdKdFAdWAr
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

generate-schema.ts declares its own empty EmailCampaignScope, which still had
an unknown index signature and no longer implements EmailCampaignScopeInterface.
The file lives outside src/, so neither lint:tsc nor eslint covers it; only the
schema generation step in CI compiles it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnYJ1LdUKw9FxdKdFAdWAr
@VPS-Obi VPS-Obi self-assigned this Sep 3, 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.

2 participants