Skip to content

fix(compiler-cli): handle directives that refer to a namespaced class in a type parameter bound#43511

Closed
JoostK wants to merge 1 commit intoangular:masterfrom
JoostK:ngtsc/ttc/namespaced-type-bound
Closed

fix(compiler-cli): handle directives that refer to a namespaced class in a type parameter bound#43511
JoostK wants to merge 1 commit intoangular:masterfrom
JoostK:ngtsc/ttc/namespaced-type-bound

Conversation

@JoostK
Copy link
Member

@JoostK JoostK commented Sep 19, 2021

The template type-checker has to emit type constructors for the
directives that are used in a template, where a type constructor's
declaration has to mirror the type parameter constraints as they were
originally declared. Therefore, the compiler analyzes whether a type
parameter constraint can be recreated, e.g. by generating imports for
any type references. Some type references cannot be recreated, in which
case the compiler has to fall back to a strategy where the type
constructor is created inline in the original source file (which comes
with a performance penalty).

There used to be an issue for type references to namespaced declarations.
The compiler is unable to emit such references such that an inline
type constructor should be used as fallback, but this did not happen.
This caused the attempt to emit the type reference to fail, as the
namespaced declaration cannot be located by the reference emitters.

This commit fixes the issue by using a stricter check to determine if a
type parameter requires an inline type constructor. The TypeScript
reflection host's isStaticallyExported logic was expanded to work for
any declaration instead of just classes, as e.g. type declarations can
also be referenced in a type parameter constraint.

Closes #43383

… in a type parameter bound

The template type-checker has to emit type constructors for the
directives that are used in a template, where a type constructor's
declaration has to mirror the type parameter constraints as they were
originally declared. Therefore, the compiler analyzes whether a type
parameter constraint can be recreated, e.g. by generating imports for
any type references. Some type references cannot be recreated, in which
case the compiler has to fall back to a strategy where the type
constructor is created inline in the original source file (which comes
with a performance penalty).

There used to be an issue for type references to namespaced declarations.
The compiler is unable to emit such references such that an inline
type constructor should be used as fallback, but this did not happen.
This caused the attempt to emit the type reference to fail, as the
namespaced declaration cannot be located by the reference emitters.

This commit fixes the issue by using a stricter check to determine if a
type parameter requires an inline type constructor. The TypeScript
reflection host's `isStaticallyExported` logic was expanded to work for
any declaration instead of just classes, as e.g. type declarations can
also be referenced in a type parameter constraint.

Closes angular#43383
@JoostK JoostK added target: patch This PR is targeted for the next patch release area: compiler Issues related to `ngc`, Angular's template compiler labels Sep 19, 2021
@google-cla google-cla bot added the cla: yes label Sep 19, 2021
@ngbot ngbot bot modified the milestone: Backlog Sep 19, 2021
@JoostK JoostK added the action: review The PR is still awaiting reviews from at least one requested reviewer label Sep 20, 2021
@JoostK JoostK marked this pull request as ready for review September 20, 2021 14:55
@atscott
Copy link
Contributor

atscott commented Sep 20, 2021

presubmit

@atscott atscott added action: merge The PR is ready for merge by the caretaker and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Sep 20, 2021
jessicajaniuk pushed a commit that referenced this pull request Sep 20, 2021
… in a type parameter bound (#43511)

The template type-checker has to emit type constructors for the
directives that are used in a template, where a type constructor's
declaration has to mirror the type parameter constraints as they were
originally declared. Therefore, the compiler analyzes whether a type
parameter constraint can be recreated, e.g. by generating imports for
any type references. Some type references cannot be recreated, in which
case the compiler has to fall back to a strategy where the type
constructor is created inline in the original source file (which comes
with a performance penalty).

There used to be an issue for type references to namespaced declarations.
The compiler is unable to emit such references such that an inline
type constructor should be used as fallback, but this did not happen.
This caused the attempt to emit the type reference to fail, as the
namespaced declaration cannot be located by the reference emitters.

This commit fixes the issue by using a stricter check to determine if a
type parameter requires an inline type constructor. The TypeScript
reflection host's `isStaticallyExported` logic was expanded to work for
any declaration instead of just classes, as e.g. type declarations can
also be referenced in a type parameter constraint.

Closes #43383

PR Close #43511
@amariq
Copy link

amariq commented Sep 23, 2021

Dunno if I should post here or in the Issues, but... After upgrading from v12.2.6 to v12.2.7 (with this fix included) my library project build now fails. Rolling only the compiler-cli package back to v12.2.6 is enough to solve the problem.

Last messages before build process fails are these:

× Compiling with Angular sources in Ivy partial compilation mode.
Unable to write a reference to <SomeInterface from another lib here>
   in <project_root>/node_modules/<some_lib_path>/<some-interface>.d.ts
   from <project root>/src/lib/<some_component_path>/<some-component>.component.ngtypecheck.ts

(SomeComponent extends another abstract component with Directive annotation on it, if this matters. Also SomeInterface not even remotely used in SomeComponent)

Any ideas?

@JoostK
Copy link
Member Author

JoostK commented Sep 23, 2021

@amariq that's a new bug, then. Please open a new issue with reproduction and I'll sort it out.

@amariq
Copy link

amariq commented Sep 23, 2021

@JoostK uh oh, okay, I'll try, but I don't even know what could lead to that. Also it involves libraries (haven't tried to build an app, yet), and both of them are internal (one uses another).

@amariq
Copy link

amariq commented Sep 24, 2021

@JoostK hello there :-)

I did some testing in our code (sorry, no issue/repro yet, will add later, still just digging up), and I think I found something.

The following part is the source of the issue:

// my-interface.ts
// this simple type
type MyValue<T, K extends keyof T = keyof T> = T | T[K];

// my-component.ts
// and type-checker fails on any other component, that uses MyComponent in its template
class MyComponent<T = any, V extends MyValue<T> = MyValue<T>> extends BaseComponent<T, V> {
  @Input()
  data: T[];

  @Input()
  value: V;
}

Any changes to MyValue don't do anything, and only removing it from MyComponent declaration or replacing with built-in types (V = any or V extends object = any) helps.

@JoostK
Copy link
Member Author

JoostK commented Sep 24, 2021

@amariq Could you share how MyValue is exported from my-interface.ts and how my-component.ts imports it?

@amariq
Copy link

amariq commented Sep 24, 2021

@JoostK of course!

// my-interface.ts
// just simple export, no namespaces or anything, after build it changes to export declare type
export type MyValue = // ...
// my-component.ts
// also simple import
import { MyValue, <and some other symbols here> } from '../my/my-interface.ts';

I also tried changing import to import type, but no effect.

@JoostK
Copy link
Member Author

JoostK commented Sep 24, 2021

Thanks a lot. That seems simple enough and unfortunately does not yet give me any pointers as to why it started failing.

@amariq
Copy link

amariq commented Sep 24, 2021

@JoostK some thoughts here.

At first I thought the reason is parameter constraints that depend on other parameters, but it doesn't seem to be the case.
I tried to edit d.ts files (assuming it would work) of the library that exports MyComponent in another library directly and changed parameters of MyComponent to replace MyValue with simpler MyValue2 = any, but it didn't help. I tried the following:

  • MyComponent<T = any, V = MyValue2>
  • MyComponent<T = any, V extends MyValue2 = any>
  • MyComponent<T = any, V extends MyValue2 = MyValue2>
  • MyComponent<T = any, V = MyValue2>
    But none worked.

Here is extended details on full type hierarchy (maybe it will bring some hints, its best I have time on right now):

  • build of some library N fails because of using some components from library A, which in its turn uses some abstract component classes from library B)

  • no namespaces used, but some places have import type

  • export class MyComponent<T = any, V extends MyValue<T> = MyValue<T>> extends BaseComponent<T, V[]> (lib A)

    • export type MyValue<T, K extends FieldOf<T> = FieldOf<T>> = T | T[K] (imported from another file)
    • export type FieldOf<T> = keyof T & string (imported from lib B)
  • export class BaseComponent<T, V extends ValueOrArray<MyValue<T>>> extends SuperBaseComponent<V> (lib A)

    • @Input() data: T[]
    • export type ValueOrArray<T> = T | T[] (imported from lib B)
  • export class SuperBaseComponent<T> extends OneMoreComponent (lib A)

    • @Input() value: T
  • export class OneMoreComponent (lib B)

@amariq
Copy link

amariq commented Sep 24, 2021

@JoostK repro here! https://github.com/amariq/angular-pr43511-bug

Will post the issue later, if necessary :)

@kahmannf
Copy link

any news on this? We have the same issue after upgrading to v12.2.7

@amariq did you open an issue for this problem?

@amariq
Copy link

amariq commented Sep 28, 2021

@kahmannf opened just now :)

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Oct 29, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

action: merge The PR is ready for merge by the caretaker area: compiler Issues related to `ngc`, Angular's template compiler cla: yes target: patch This PR is targeted for the next patch release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generic component inheritance fails in AOT since v12 upgrade

4 participants