Which @angular/* package(s) are relevant/related to the feature request?
compiler-cli, compiler
Description
Angular template type checking infers a generic component type parameter from a signal model() input even when the model value type is wrapped with TypeScript NoInfer.
This makes it impossible to express an API where an optional discriminant input controls the value type, while the omitted discriminant should keep the component generic default.
For example, a date picker can have the following API:
- no mode input means single
mode="range" means range value
However, the template checker accepts a range value without the mode input, because it infers the generic type from the value binding.
Minimal reproduction
import {Component, input, model} from '@angular/core';
type Mode = 'single' | 'range';
type ValueByMode = {
single: string;
range: readonly [string, string];
};
type Value<T extends Mode = 'single'> = ValueByMode[T];
@Component({
selector: 'app-picker',
standalone: true,
template: '',
})
export class Picker<T extends Mode = 'single'> {
readonly value = model<NoInfer<Value<T>> | null>(null);
readonly mode = input<T>();
}
@Component({
selector: 'app-root',
standalone: true,
imports: [Picker],
template: `
<!-- Expected error: mode is omitted, so T should default to "single" -->
<app-picker [(value)]="range" />
<!-- OK -->
<app-picker [(value)]="single" />
<!-- OK -->
<app-picker
mode="range"
[(value)]="range"
/>
`,
})
export class App {
single: string | null = null;
range: readonly [string, string] | null = null;
}
Expected behavior
The following template should fail type checking:
<app-picker [(value)]="range" />
Because mode is omitted, the generic type parameter should use its default value:
T = 'single'
So value should be checked as:
string | null
Actual behavior
The template passes type checking.
It looks like Angular infers:
T = 'range'
from the [(value)] binding, even though the model value type uses NoInfer<Value<T>>.
Why this matters
This prevents component authors from building discriminated generic APIs where one input is the source of truth for the generic parameter and other inputs should only be checked against the inferred/default generic type.
In normal TypeScript APIs, NoInfer can be used for this kind of inference control.
{
"angularCompilerOptions": {
"strictTemplates": true,
"strictContextGenerics": true
}
}
Which @angular/* package(s) are relevant/related to the feature request?
compiler-cli, compiler
Description
Angular template type checking infers a generic component type parameter from a signal
model()input even when the model value type is wrapped with TypeScript NoInfer.This makes it impossible to express an API where an optional discriminant input controls the value type, while the omitted discriminant should keep the component generic default.
For example, a date picker can have the following API:
mode="range"means range valueHowever, the template checker accepts a range value without the mode input, because it infers the generic type from the value binding.
Minimal reproduction
Expected behavior
The following template should fail type checking:
<app-picker [(value)]="range" />Because mode is omitted, the generic type parameter should use its default value:
T = 'single'So value should be checked as:
string | nullActual behavior
The template passes type checking.
It looks like Angular infers:
T = 'range'from the
[(value)]binding, even though the model value type usesNoInfer<Value<T>>.Why this matters
This prevents component authors from building discriminated generic APIs where one input is the source of truth for the generic parameter and other inputs should only be checked against the inferred/default generic type.
In normal TypeScript APIs, NoInfer can be used for this kind of inference control.
{ "angularCompilerOptions": { "strictTemplates": true, "strictContextGenerics": true } }