Skip to content

Template compiles with strict but not with full #35650

Open

Description

@jgbpercy

🐞 bug report

Affected Package

The issue is caused by package @angular/compiler (I guess)

Is this a regression?

Yes, kiiiiinda

With enableIvy false (v9 and I'm almost certain v8 as well), changing the final non-null assert (!) in the template into a null propagation (?) makes the error go away.

Description

The code in the reproduction below compiles with "strictTemplate": true in tsconfig, but not with "fullTemplateTypeCheck": true only.

As far as I understand, anything that compiles with strict should compile with full? As an aside, I think it would be good if that was made clear one way or another in this section of the docs (happy to raise a separate issue).

Furthermore, the error in full mode doesn't make any sense, as far as I understand.

As noted above, there is a similar issue with Ivy disabled, but for reasons I don't understand it goes away when using null propagation rather than non-null assertion.

🔬 Minimal Reproduction

https://github.com/jgbpercy/AngularCompilesOnlyStrict
(currently the repo should be in a non-compiling state - add "strictTemplate": true to tsconfig angularCompilerOptions and it should compile)

🔥 Exception or Error


ERROR in src/app/app.component.html:2:12 - error TS2339: Property 'doDar' does not exist on type 'never'.

2   
{{ (thinger!.innerObs | async)!.doDar }}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/app/app.component.ts:14:16 14 templateUrl: './app.component.html', ~~~~~~~~~~~~~~~~~~~~~~ Error occurs in the template of component AppComponent.

🌍 Your Environment

Angular Version:


Angular CLI: 9.0.3
Node: 10.15.0
OS: win32 x64

Angular: 9.0.2
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Ivy Workspace: Yes

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.900.3
@angular-devkit/build-angular     0.900.3
@angular-devkit/build-optimizer   0.900.3
@angular-devkit/build-webpack     0.900.3
@angular-devkit/core              9.0.3
@angular-devkit/schematics        9.0.3
@angular/cli                      9.0.3
@ngtools/webpack                  9.0.3
@schematics/angular               9.0.3
@schematics/update                0.900.3
rxjs                              6.5.4
typescript                        3.7.5
webpack                           4.41.2

Anything else relevant?
The language service thinks the code is fine and picks up the type ok!

So I'm guessing in full the first | async is polluting the type it outputs with a union to null, and then the second | async is chewing up horribly on that somehow. Whereas in strict it has enough information to know that null isn't part of the relevant type at the right time.

Activity

  1. JoostK commented on Feb 24, 2020

    @JoostK
    Member

    I think what's going in here is that with only fullTemplateTypeCheck set, the strictInputTypes option is disabled so the type of thinger is inferred as any. The async pipe has various overloads for its transform method, the first one being:

        transform<T>(obj: null): null;

    Therefore, the type of (thinger!.innerObs | async) is inferred as null given that this first overload matches the any type that is passed in. The non-null operator will then "remove" null and undefined from the type, and removing null from null is equal to the type never.

    I am not quite sure why using the ? does not have the same behavior, I'd have to look into that more.

    This can probably be fixed using a different overload overing in the async pipe's transform declaration.

  2. modified the milestone: Backlog on Feb 24, 2020
  3. alxhub commented on Feb 26, 2020

    @alxhub
    Member

    I don't know if going through a different overload is going to help.

    I reordered them here and played around with the inference: http://www.typescriptlang.org/play/#code/CYUwxgNghgTiAEkoGdnwPICNkhgNykwhAB4AVAPngG94BfAKAaVXgEFkBPAOzAAUAlgAcE1JvHgAXGFG7IAZgHsYAW3IUAFIswArAFwZsuAkVKUAPtwCuECOavdQ8gdxDAAlAbKWbEANwMEtKyCspqlFq6BnwwiioCOOo+tvaOIM6uHl7J-oFSMnJKquqR+vDWtp7lvgFBBaHFEdplDk4ublWt6e3AtRL5IUUqpQZYOPiExCSynFTm8DFxCaQzc9W28PNdGR0GMzR5EoyMzIpykvBC8AC85SAA7uxcvIIiGu4BoEhwiGfIFwAPPbcTgBMB-C5wNC3IQAOmChTCGgBH3gAHo0fAHABrbiKe7cIA

    However, even if we go through the Observable<T> branch, what ends up happening is that TypeScript tries to solve Observable<T> = any for T, and since it has no context for inferring T in this case, it defaults to unknown, which is only slightly better than never and not at all helpful to this case - doDar is no more present on unknown than never.

    The only way I could make this work is with magic type hacks:

    type IsAny<T> = 0 extends (1 & T) ? any : never;
    
    class AsyncPipe {
        transform<T extends IsAny<T>>(obj: T): any;
        transform<T>(obj: null): null;
        transform<T>(obj: undefined): undefined;
        ...

    This adds an overload signature to AsyncPipe.transform specifically for the case where someone passes any.

  4. reopened this on Feb 26, 2020
  5. jgbpercy commented on Feb 26, 2020

    @jgbpercy
    Author

    Thanks both for the interesting explanations! So the problem is you want an overload to match any, but only any...

    Just to be clear, talking about our code specifically, we'll be using strictTemplate for Ivy, and in our library code that is still (for now) non-Ivy compiled, we can drop back to the null propagation workaround that works when enableIvy is false. So this isn't something that's actively a problem for us I don't think. I'm personally fine with this being closed as not worth fixing, but I'll leave it up to you.

  6. alxhub commented on Feb 26, 2020

    @alxhub
    Member

    I think it's worth fixing, for sure.

  7. added
    P4A relatively minor issue that is not relevant to core functions
    and removed on Oct 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      Sponsor
      SponsoredKunjungi sekarang
      Promo