From 6829b2e1472e99f53894d81b0c7c8ad58a201711 Mon Sep 17 00:00:00 2001 From: cexbrayat Date: Thu, 18 Jun 2026 17:36:54 +0200 Subject: [PATCH] fix(core): allow static attributes for explicit input transforms This is a follow-up to #67997, which allowed explicit read generics with input transforms, such as `input(false, {transform: booleanAttribute})`. That fixed the declaration, but static template attributes like `dismissible="true"` and bare `dismissible` were still checked as strings against the read type. Allow the fallback write type to include static attribute strings so these template forms compile. --- goldens/public-api/core/index.api.md | 4 +- .../test/ngtsc/authoring_inputs_spec.ts | 60 +++++++++++++++++++ packages/core/src/authoring/input/input.ts | 11 ++-- .../authoring/signal_input_signature_test.ts | 8 +-- 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/goldens/public-api/core/index.api.md b/goldens/public-api/core/index.api.md index 271578483f5c..b39c3e1242e7 100644 --- a/goldens/public-api/core/index.api.md +++ b/goldens/public-api/core/index.api.md @@ -1029,8 +1029,8 @@ export interface InputFunction { (initialValue: undefined, opts: InputOptionsWithoutTransform): InputSignal; (initialValue: T, opts: InputOptionsWithTransform): InputSignalWithTransform; (initialValue: undefined, opts: InputOptionsWithTransform): InputSignalWithTransform; - (initialValue: T, opts: InputOptionsWithTransform): InputSignalWithTransform; - (initialValue: undefined, opts: InputOptionsWithTransform): InputSignalWithTransform; + (initialValue: T, opts: InputOptionsWithTransform): InputSignalWithTransform; + (initialValue: undefined, opts: InputOptionsWithTransform): InputSignalWithTransform; required: { (opts?: InputOptionsWithoutTransform): InputSignal; (opts: InputOptionsWithTransform): InputSignalWithTransform; diff --git a/packages/compiler-cli/test/ngtsc/authoring_inputs_spec.ts b/packages/compiler-cli/test/ngtsc/authoring_inputs_spec.ts index 6122cfd3720f..c5831b81254f 100644 --- a/packages/compiler-cli/test/ngtsc/authoring_inputs_spec.ts +++ b/packages/compiler-cli/test/ngtsc/authoring_inputs_spec.ts @@ -320,6 +320,66 @@ runInEachFileSystem(() => { ); }); + it('should allow text attributes for explicit signal inputs with booleanAttribute transform', () => { + env.write( + 'test.ts', + ` + import {booleanAttribute, Component, Directive, input} from '@angular/core'; + + @Directive({ + selector: '[directiveName]', + }) + export class TestDir { + dismissible = input(true, {transform: booleanAttribute}); + } + + @Component({ + template: \` +
+
+
+ \`, + imports: [TestDir], + }) + export class TestComp { + } + `, + ); + + const diagnostics = env.driveDiagnostics(); + expect(diagnostics).toEqual([]); + }); + + it('should allow text attributes for explicit signal inputs with numberAttribute transform', () => { + env.write( + 'test.ts', + ` + import {Component, Directive, input, numberAttribute} from '@angular/core'; + + @Directive({ + selector: '[directiveName]', + }) + export class TestDir { + count = input(0, {transform: numberAttribute}); + } + + @Component({ + template: \` +
+
+
+ \`, + imports: [TestDir], + }) + export class TestComp { + } + `, + ); + + const diagnostics = env.driveDiagnostics(); + expect(diagnostics).toEqual([]); + }); + it('should report unset required inputs', () => { env.write( 'test.ts', diff --git a/packages/core/src/authoring/input/input.ts b/packages/core/src/authoring/input/input.ts index dcc4394f8a89..6f1d63962490 100644 --- a/packages/core/src/authoring/input/input.ts +++ b/packages/core/src/authoring/input/input.ts @@ -79,17 +79,20 @@ export interface InputFunction { ): InputSignalWithTransform; /** * Declares an input of type `T` with an initial value and a transform function - * that accepts values of the same type. + * that accepts values of the same type, or string values from static attributes. */ - (initialValue: T, opts: InputOptionsWithTransform): InputSignalWithTransform; + ( + initialValue: T, + opts: InputOptionsWithTransform, + ): InputSignalWithTransform; /** * Declares an input of type `T|undefined` without an initial value and with a transform - * function that accepts values of the same type. + * function that accepts values of the same type, or string values from static attributes. */ ( initialValue: undefined, opts: InputOptionsWithTransform, - ): InputSignalWithTransform; + ): InputSignalWithTransform; /** * Initializes a required input. diff --git a/packages/core/test/authoring/signal_input_signature_test.ts b/packages/core/test/authoring/signal_input_signature_test.ts index 5f1d0a060aba..1e929326911c 100644 --- a/packages/core/test/authoring/signal_input_signature_test.ts +++ b/packages/core/test/authoring/signal_input_signature_test.ts @@ -102,15 +102,15 @@ export class InputSignatureTest { transform: (v: string | boolean) => '', }); - /** boolean, boolean */ + /** boolean, string | boolean */ explicitReadWithBooleanAttributeTransform = input(false, {transform: booleanAttribute}); - /** number, number */ + /** number, string | number */ explicitReadWithNumberAttributeTransform = input(0, {transform: numberAttribute}); - /** boolean | undefined, boolean | undefined */ + /** boolean | undefined, string | boolean | undefined */ explicitReadWithUndefinedInitialBooleanAttributeTransform = input(undefined, { transform: booleanAttribute, }); - /** number | undefined, number | undefined */ + /** number | undefined, string | number | undefined */ explicitReadWithUndefinedInitialNumberAttributeTransform = input(undefined, { transform: numberAttribute, });