0 day streak
javascript / expert
Snippet

Narrowing Component Inputs with Required Signal Inputs and Transform Functions

`input.required<Severity, string>({ transform: toSeverity })` declares two distinct types for the same input: `string` is what a template author is allowed to bind (`[severity]="someString"`), while `Severity` is what the component body reads back via `severity()`. The `transform` function runs synchronously at binding time and is the only bridge between them — the compiler enforces that the transform's parameter type matches the write type and its return type matches the read type, so a mismatched transform fails to compile rather than silently letting an unvalidated string through. Because the input is `required`, TypeScript also refuses to compile any template that instantiates `AlertBannerComponent` without binding `severity`.

snippet.js
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { Component, input } from '@angular/core';
type Severity = 'low' | 'medium' | 'high';
function toSeverity(value: string): Severity {
const normalized = value.toLowerCase();
if (normalized === 'low' || normalized === 'medium' || normalized === 'high') {
return normalized;
}
throw new Error(`Invalid severity value: "${value}"`);
}
@Component({
selector: 'app-alert-banner',
standalone: true,
template: `<div [class]="'banner-' + severity()">{{ message() }}</div>`,
})
export class AlertBannerComponent {
severity = input.required<Severity, string>({ transform: toSeverity });
message = input.required<string>();
}
angular
Breakdown
1
type Severity = 'low' | 'medium' | 'high';
The narrow read-side type — the only three values `severity()` can ever produce inside the component after the transform runs.
2
function toSeverity(value: string): Severity
The transform function itself; its parameter type must match the input's write type and its return type must match the read type, or the compiler rejects the `input.required` call.
3
throw new Error(`Invalid severity value: "${value}"`);
Since transforms run synchronously during change detection, an invalid binding surfaces immediately as a thrown error rather than silently becoming an unnarrowed string.
4
severity = input.required<Severity, string>({ transform: toSeverity });
The two generic parameters split read type from write type; omitting `required` would make `severity()` possibly `undefined` even after a successful transform.