Skip to content

Commit ac9c544

Browse files
crisbetothePunderWoman
authored andcommitted
refactor(core): assert writable signal in two-way property instruction (angular#54252)
Asserts that the value is a `WritableSignal`, rather than a `Signal`, in the `twoWayProperty` instruction. PR Close angular#54252
1 parent 243b94c commit ac9c544

3 files changed

Lines changed: 12 additions & 12 deletions

File tree

packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {AST, ASTWithSource, BindingPipe, BindingType, ParseSourceSpan, PropertyRead, PropertyWrite, R3Identifiers, SafePropertyRead, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler';
9+
import {AST, ASTWithSource, BindingPipe, ParseSourceSpan, PropertyRead, PropertyWrite, R3Identifiers, SafePropertyRead, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler';
1010
import ts from 'typescript';
1111

1212
import {AbsoluteFsPath} from '../../file_system';

packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {AST, BindingPipe, BindingType, BoundTarget, Call, createCssSelectorFromNode, CssSelector, DYNAMIC_TYPE, ExpressionType, ImplicitReceiver, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, R3Identifiers, SafeCall, SafePropertyRead, SchemaMetadata, SelectorMatcher, ThisReceiver, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstBoundText, TmplAstDeferredBlock, TmplAstDeferredBlockTriggers, TmplAstElement, TmplAstForLoopBlock, TmplAstForLoopBlockEmpty, TmplAstHoverDeferredTrigger, TmplAstIcu, TmplAstIfBlock, TmplAstIfBlockBranch, TmplAstInteractionDeferredTrigger, TmplAstNode, TmplAstReference, TmplAstSwitchBlock, TmplAstSwitchBlockCase, TmplAstTemplate, TmplAstText, TmplAstTextAttribute, TmplAstVariable, TmplAstViewportDeferredTrigger, TransplantedType, TypeofExpr, WrappedNodeExpr} from '@angular/compiler';
9+
import {AST, BindingPipe, BindingType, BoundTarget, Call, createCssSelectorFromNode, CssSelector, DYNAMIC_TYPE, ImplicitReceiver, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, R3Identifiers, SafeCall, SafePropertyRead, SchemaMetadata, SelectorMatcher, ThisReceiver, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstBoundText, TmplAstDeferredBlock, TmplAstDeferredBlockTriggers, TmplAstElement, TmplAstForLoopBlock, TmplAstForLoopBlockEmpty, TmplAstHoverDeferredTrigger, TmplAstIcu, TmplAstIfBlock, TmplAstIfBlockBranch, TmplAstInteractionDeferredTrigger, TmplAstNode, TmplAstReference, TmplAstSwitchBlock, TmplAstSwitchBlockCase, TmplAstTemplate, TmplAstText, TmplAstTextAttribute, TmplAstVariable, TmplAstViewportDeferredTrigger, TransplantedType} from '@angular/compiler';
1010
import ts from 'typescript';
1111

1212
import {Reference} from '../../imports';
@@ -792,12 +792,12 @@ class TcbDirectiveInputsOp extends TcbOp {
792792
dirId, ts.factory.createIdentifier(fieldName));
793793
}
794794

795+
// For signal inputs, we unwrap the target `InputSignal`. Note that
796+
// we intentionally do the following things:
797+
// 1. keep the direct access to `dir.[field]` so that modifiers are honored.
798+
// 2. follow the existing pattern where multiple targets assign a single expression.
799+
// This is a significant requirement for language service auto-completion.
795800
if (isSignal) {
796-
// For signal inputs, we unwrap the target `InputSignal`. Note that
797-
// we intentionally do the following things:
798-
// 1. keep the direct access to `dir.[field]` so that modifiers are honored.
799-
// 2. follow the existing pattern where multiple targets assign a single expression.
800-
// This is a significant requirement for language service auto-completion.
801801
const inputSignalBrandWriteSymbol = this.tcb.env.referenceExternalSymbol(
802802
R3Identifiers.InputSignalBrandWriteType.moduleName,
803803
R3Identifiers.InputSignalBrandWriteType.name);

packages/core/src/render3/instructions/two_way.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
import {bindingUpdated} from '../bindings';
1010
import {SanitizerFn} from '../interfaces/sanitization';
1111
import {RENDERER} from '../interfaces/view';
12-
import {isSignal} from '../reactivity/api';
13-
import {isWritableSignal} from '../reactivity/signal';
12+
import {isWritableSignal, WritableSignal} from '../reactivity/signal';
1413
import {getCurrentTNode, getLView, getSelectedTNode, getTView, nextBindingIndex} from '../state';
1514

1615
import {listenerInternal} from './listener';
@@ -31,10 +30,11 @@ import {elementPropertyInternal, storePropertyBindingMetadata} from './shared';
3130
* @codeGenApi
3231
*/
3332
export function ɵɵtwoWayProperty<T>(
34-
propName: string, value: T, sanitizer?: SanitizerFn|null): typeof ɵɵtwoWayProperty {
33+
propName: string, value: T|WritableSignal<T>,
34+
sanitizer?: SanitizerFn|null): typeof ɵɵtwoWayProperty {
3535
// TODO(crisbeto): perf impact of re-evaluating this on each change detection?
36-
if (isSignal(value)) {
37-
value = value() as T;
36+
if (isWritableSignal(value)) {
37+
value = value();
3838
}
3939

4040
const lView = getLView();

0 commit comments

Comments
 (0)