Skip to content

Conversation

@JeanMeche
Copy link
Member

This uses an internal property for prototyping purposes so we don't commit to an API at the moment.

fixes #65894

@JeanMeche JeanMeche force-pushed the devtools/internal-signals branch from 562b23d to 52a7153 Compare December 16, 2025 14:42
This uses an internal property for prototyping purposes so we don't commit to an API at the moment.

fixes angular#65894
@JeanMeche JeanMeche force-pushed the devtools/internal-signals branch from 52a7153 to 84327b3 Compare December 16, 2025 14:48
/**
* @internal
*/
isInternal?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider: Would ɵisInternal improve typing without expanding our public API contract?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@internal actually effectively removes the prop from the public API. (it's not present in the generated d.ts.

private readonly _submittedReactive = signal(false);
readonly _submitted = computed(
() => this._submittedReactive(),
...(ngDevMode ? [{isInternal: true} as any] : []),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider: It seems easy to forget the ngDevMode bits here (or misuse it like isInternal: ngDevMode ? true : undefined), especially if we're doing this manually at each call site. We also won't have extensive tests that this is a zero-cost addition in production.

IIRC, the debug name transform wraps an explicit name in an ngDevMode conditional (might be worth double checking that). What if we boxed both debugName and isInternal into a shared debugInfo object and tree shake that value once rather than having to do each one individually?

// Definition

interface DebugInfo {
  name?: string;
  isInternal?: boolean;
}

declare function signal<T>(initial: T, opts?: {debugInfo?: DebugInfo}): WritableSignal<T>;

// Dev writes:

const foo = signal(0, {
  debugInfo: {isInternal: true},
});

// Transformed to (using our existing infra, just tweaked for `debugInfo` instead of `debugName`):

const foo = signal(0, {
  ...(ngDevMode ? {
    debugInfo: {
      name: 'foo',
      isInternal: true,
    },
  } : {})
});

This way we don't need to think about tree shaking at each call site, we just annotate which signals are internal.

I know we're just experimenting here for now, so I'm fine to come back to this later. I'm mainly calling out that there might be value in a DebugInfo abstraction which allows us to introduce new debug state in the future without having to reinvent the tree shaking solution we need for production.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

devtools(signal graph): internal framework signals can overwhelm application signals

3 participants