Library inputs supporting `async` pipe
===
There is no documentation on how the `async` pipe works under the hood. To understand how the problem surfaces, we need to understand that the `async` pipe returns `null` if the specified observable did not emit a value yet.
Meaning that the return type of the `async` pipe is `T | null`. This means that every input
needs to accept `null` because otherwise the `async` pipe does not work.
### Why didn't this surface before Ivy?
Before Ivy, the compiler-cli had no working implementation of strict input type checking. This means that the issue never showed up during compilation. i.e. consumers simply used the `async` pipe and never got a compilation failure (even if the input did not support `null`)
Hence the issue could have only surfaced at runtime. e.g. if the library only expected strings and called `toUpperCase()` on the input value (`@Input() message: string = 'default';`)
### When does this surface in Ivy?
**TL;DR**: Issue is not a blocker for V9 because input type checking is not enabled by default.
The issue that type checking complains about the `T | null` not being assignable to
an input only shows up if the consumer application has `strictTemplates` enabled and
`strictNullChecks`.
This is because by default in version 9, ngtsc does not check templates strictly (for forward-compatibility of Ivy). Though in the future strict template type checking might be the default, or developers already opt-in manually in v9. In those cases, if the consumer application has **`strictNullChecks`** enabled, ngtsc will report a type checking failure for cases like that:
```htmlmixed=
<button mat-button [disabled]="isDisabled | async"></button>
```
```typescript=
export class MatButton {
@Input() disabled: boolean;
}
```
`null` or `undefined` are never assignable to `MatButton#disabled`. Though these two values would be valid if `strictNullChecks` are disabled. This specific issue can be widened into a more general issue where libraries behave differently based on the **`strictNullChecks`** flag.
### How can developers work around?
To work around these issues there is a ngtsc strictness flag called **`strictNullInputBindings`**. This flag can be set to `false` in order to essentially turn of `strictNullChecks` for input bindings. Though this workaround is not always ideal since some libraries do have inputs which _explicitly_ do not accept `null` or `undefined`.
Using a flag to avoid the template type checking failure is not a real long-term solution. This is because the error is valid and the input will be assigned to `null`. Using the flag therefore can hide valid errors where the input actually _does not_ expect `null`.
### Does adding `null` to every input in libraries solve that?
Yes. This would definitely solve that issue. Just to be clear: `null` needs to be added explicitly to every input because we want the input to behave consistently regardless of `strictNullChecks`.
For standard inputs, the `null` type should be added. Using the `ngInputAccept_` members is not sufficient because the actual input type would still be non-nullable. This is incorrect and can cause bugs because the input _could_ be validly set to `null`.
Adding `null` to every input brings up a **concern** where this change potentially causes a lot of breaking changes since consumers with `strictNullChecks` can run into compilation errors. e.g.
```typescript=
button.myInput.toString(); // worked before
button.myInput.toString(); // myInput is possibly "null" or "undefined".
```
Also the overall solution would need to be documented since most library authors are likely not aware of the `async` pipe issue. Basically library authors are *forced* to explicitly support `null`. And due to the way `strictNullChecks` work, they are even technically forced to add it _explicitly_. (not relying on the fact that w/o strict null checks, null is always assignable)
### What if certain inputs never should support `null`?
There are two cases: (1) the input should be reconsidered to actually accept `null` for better consistency regardless of consumers using `strictNullChecks`. And the (2) case, which is more common: Input actually should *never* accept null by intention.
In the (2) case, there is no way to make the async pipe work currently. There are various options framework, the library authors or the consumers can take to get it work:
| Group | Proposals |
| -------- | -------- |
| Framework team | 1) `async` pipe should not set binding if no value has been emitted yet. <br/> 2) Introduce a concept of `@RequiredInput(defaultVal)` with a default value. |
| Library authors | Accepting `null` in the type, but managing the default value manually. This will be similar to coercion inputs and either involve payload-size affecting setters, or handling default values through `ngOnChanges` |
| Consumers | Manually ensuring that `null` is not assigned to the input. e.g. by having fallbacks like `[disabled]="(isDisabled$ | async) || false"` |
### Will this be a lot of effort for libraries?
Existing libraries that were not aware of this issue (since it will be only an issue now with strict input type checking), need to **audit all** of their inputs and add `null` explicitly.
This will be a lot of work based on the amount of inputs. Special attention is necessary for each input, since in some situations `null` might not be correctly handled at runtime. i.e. needing logic changes.
### Possible standpoints/questions
**Framework team**:
* Should the `async` pipe behave differently? i.e. not assign a value to the input until there is actually an value emitted?
**Library authors**:
* Is it actually the responsibility of the library to ensure that every input is compatible with the `async` pipe?
* Adding `null` or `undefined` to every input is very cumbersome. How can this be enforced for libraries? how will this be communicated to new library authors?
* "We don't care about consumers of the library w/o strict null checks enabled". We require strict null checks and some inputs _should_ not accept `null`. Not sure how the async pipe will work for such inputs?
**Library consumers**:
* The consumer expectation is that the library behaves the same regardless of strict null checks
* Expects the `async` pipe to just work. Shifting the responsibility of making the pipe work to the consumer feels wrong. examples:
```htmlmixed=
<!-- dataSource input does not accept null. -->
<mat-tree [dataSource]="(dataSource$ | async) || emptyDataSource"></mat-tree>
<button mat-button [disabled]="!!(isDisabled$ | async)"></button>
<!-- in some cases there is no reasonable fallback value? -->
<other-comp [someInput]="(value$ | async ) || ??"></other-comp>
```
---
### Solution for angular/components:
Audit inputs and categorize based on whether they should support `null` or not. For the ones which should not support `null`, we act based on the outcome of `#what-if-certain-inputs-never-should-support-null`.