docs: deprecate input setter coercion fields (#43506)

Since the `strictTemplates` flag has been introduced in Angular the
compiler has been able to type-check input bindings to the declared
input type of the corresponding directive. When a getter/setter pair is
being used for the input it may be desirable to let the setter accept a
broader set of types than what is returned by the getter, for example
when the setter first converts the input value. However, until
TypeScript 4.3 a getter/setter pair was required to have identical types
so this pattern could not be accurately declared.

To mitigate this limitation, it was made possible to declare
input setter coercion fields in directives that are used when
type-checking input bindings. However, since TypeScript 4.3 the
limitation has been removed; setters can now accept a wider type than
what is returned by the getter. This means that input coercion fields
are no longer needed, as their effects can be achieved by widening the
type of the setter.

PR Close #43506
This commit is contained in:
JoostK
2021-09-18 21:41:15 +02:00
committed by Alex Rickabaugh
parent 3e37e8979d
commit 81d09d14dc
3 changed files with 59 additions and 3 deletions
+1 -1
View File
@@ -618,7 +618,7 @@ For example, to avoid `Object is possibly 'undefined'` error in the template abo
Using `*ngIf` allows the TypeScript compiler to infer that the `person` used in the binding expression will never be `undefined`.
For more information about input type narrowing, see [Input setter coercion](guide/template-typecheck#input-setter-coercion) and [Improving template type checking for custom directives](guide/structural-directives#directive-type-checks).
For more information about input type narrowing, see [Improving template type checking for custom directives](guide/structural-directives#directive-type-checks).
### Non-null type assertion operator
+50
View File
@@ -48,6 +48,7 @@ v12 - v15
| template syntax | [`<template>`](#template-tag) | <!--v7--> v11 |
| polyfills | [reflect-metadata](#reflect-metadata) | <!--v8--> v11 |
| npm package format | [`esm5` and `fesm5` entry-points in @angular/* npm packages](guide/deprecations#esm5-fesm5) | <!-- v9 --> v11 |
| `@angular/compiler-cli` | [Input setter coercion](#input-setter-coercion) | <!--v13--> v15 |
| `@angular/core` | [`defineInjectable`](#core) | <!--v8--> v11 |
| `@angular/core` | [`entryComponents`](api/core/NgModule#entryComponents) | <!--v9--> v11 |
| `@angular/core` | [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | <!--v9--> v11 |
@@ -475,6 +476,55 @@ For full rationale and discussion behind this deprecation, see [RFC: Internet Ex
**Note**: IE11 will be supported in Angular v12 LTS releases through November 2022.
-->
{@a input-setter-coercion}
### Input setter coercion
Since the `strictTemplates` flag has been introduced in Angular the compiler has been able to type-check input bindings to the declared input type of the corresponding directive.
When a getter/setter pair is being used for the input it may be desirable to let the setter accept a broader set of types than what is returned by the getter, for example when the setter first converts the input value.
However, until TypeScript 4.3 a getter/setter pair was required to have identical types so this pattern could not be accurately declared.
To mitigate this limitation, it was made possible to declare [input setter coercion fields](guide/template-typecheck#input-setter-coercion) in directives that are used when type-checking input bindings.
However, since [TypeScript 4.3](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-3.html#separate-write-types-on-properties) the limitation has been removed; setters can now accept a wider type than what is returned by the getter.
This means that input coercion fields are no longer needed, as their effects can be achieved by widening the type of the setter.
For example, the following directive:
```typescript
@Component({...})
class SubmitButton {
private _disabled: boolean;
@Input()
get disabled(): boolean {
return this._disabled;
}
set disabled(value: boolean) {
this._disabled = (value === '') || value;
}
static ngAcceptInputType_disabled: boolean|'';
}
```
can be refactored as follows:
```typescript
@Component({...})
class SubmitButton {
private _disabled: boolean;
@Input()
get disabled(): boolean {
return this._disabled;
}
set disabled(value: boolean|'') {
this._disabled = (value === '') || value;
}
}
```
{@a deprecated-cli-flags}
## Deprecated CLI APIs and Options
+8 -2
View File
@@ -207,7 +207,7 @@ Here, during type checking of the template for `AppComponent`, the `[user]="sele
Therefore, Angular assigns the `selectedUser` property to `UserDetailComponent.user`, which would result in an error if their types were incompatible.
TypeScript checks the assignment according to its type system, obeying flags such as `strictNullChecks` as they are configured in the application.
Avoid run-time type errors by providing more specific in-template type requirements to the template type checker. Make the input type requirements for your own directives as specific as possible by providing template-guard functions in the directive definition. See [Improving template type checking for custom directives](guide/structural-directives#directive-type-checks), and [Input setter coercion](#input-setter-coercion) in this guide.
Avoid run-time type errors by providing more specific in-template type requirements to the template type checker. Make the input type requirements for your own directives as specific as possible by providing template-guard functions in the directive definition. See [Improving template type checking for custom directives](guide/structural-directives#directive-type-checks) in this guide.
### Strict null checks
@@ -299,7 +299,7 @@ set disabled(value: boolean) {
```
It would be ideal to change the type of `value` here, from `boolean` to `boolean|''`, to match the set of values which are actually accepted by the setter.
TypeScript requires that both the getter and setter have the same type, so if the getter should return a `boolean` then the setter is stuck with the narrower type.
TypeScript prior to version 4.3 requires that both the getter and setter have the same type, so if the getter should return a `boolean` then the setter is stuck with the narrower type.
If the consumer has Angular's strictest type checking for templates enabled, this creates a problem: the empty string `''` is not actually assignable to the `disabled` field, which creates a type error when the attribute form is used.
@@ -322,6 +322,12 @@ class SubmitButton {
}
```
<div class="alert is-important">
Since TypeScript 4.3, the setter could have been declared to accept `boolean|''` as type, making the input setter coercion field obsolete. As such, input setters coercion fields have been deprecated.
</div>
This field does not need to have a value. Its existence communicates to the Angular type checker that the `disabled` input should be considered as accepting bindings that match the type `boolean|''`. The suffix should be the `@Input` _field_ name.
Care should be taken that if an `ngAcceptInputType_` override is present for a given input, then the setter should be able to handle any values of the overridden type.