Typing the required input as ReadonlyArray<CartLine> with a CartLine interface whose fields are all readonly makes accidental in-place mutation a compile-time error rather than a runtime bug, which matters because signal-based change detection compares references, not deep contents. If a child accidentally called lines().push(...) on a plain array, Angular would never detect the change since the array reference stays the same, silently desyncing the UI from the actual cart state. Declaring both the array and its elements immutable forces callers to always produce a new array on update, which is exactly what triggers computed() to recalculate.
import { Component, input, computed, Signal } from '@angular/core';interface CartLine {readonly sku: string;readonly qty: number;readonly price: number;}@Component({selector: 'app-cart-summary',standalone: true,template: `<p>Total: {{ total() | currency }}</p>`,})export class CartSummaryComponent {lines = input.required<ReadonlyArray<CartLine>>();protected readonly total: Signal<number> = computed(() =>this.lines().reduce((sum, line) => sum + line.qty * line.price, 0));protected readonly isEmpty: Signal<boolean> = computed(() => this.lines().length === 0);}