angular: compiler-cli: lowering_expression transformer does not handle forwardRef like function

@Component({
  selector: 'app-test-component',
  templateUrl: './test-component.component.html',
  styleUrls: ['./test-component.component.css']
})
export class TestComponentComponent {
  prop: string;
}

export function MyPropDecorator(value: any) {
  return (target: Object, key: string) => {  }
}

export class MyClass {
  @MyPropDecorator(() => TestComponentComponent)
  prop: string;
}
SHLASSAF-M-4099:testproj shlomiassaf$ ng build --aot --build-optimizer
Date: 2017-11-06T04:02:00.134Z                                                     
Hash: 957bab95fbd81c3b3b0f
Time: 3658ms
chunk {inline} inline.bundle.js, inline.bundle.js.map (inline) 5.83 kB [entry] [rendered]
chunk {main} main.bundle.js, main.bundle.js.map (main) 303 bytes [initial] [rendered]
chunk {polyfills} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 323 bytes [initial] [rendered]
chunk {styles} styles.bundle.js, styles.bundle.js.map (styles) 11.3 kB [initial] [rendered]

ERROR in Error: TypeError: Cannot read property 'kind' of undefined
    at nodeCanBeDecorated (/Users/shlomiassaf/Desktop/Cisco/personal/testproj/node_modules/typescript/lib/typescript.js:7805:35)

Its not @angualr-devkit/build-optimizer, i removed it from the command line, still errors.

This will work:

export function MyPropDecorator(value: any) {
  return (target: Object, key: string) => {  }
}

export class MyClass {
  @MyPropDecorator(TestComponentComponent)
  prop: string;
}

Being explicit won’t help

export function MyPropDecorator(value: () => typeof TestComponentComponent) {
  return (target: Object, key: string) => {  }
}

export class MyClass {
  @MyPropDecorator(() => TestComponentComponent)
  prop: string;
}

BUT, being static will help:

export function MyPropDecorator(value: () => typeof TestComponentComponent) {
  return (target: Object, key: string) => {  }
}

export function MyGetter() {
  return TestComponentComponent;
}

export class MyClass {
  @MyPropDecorator(MyGetter)
  prop: string;
}

Thanks!

Angular CLI: 1.5.0
Node: 8.4.0
OS: darwin x64
Angular: 5.0.0
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

@angular/cli: 1.5.0
@angular-devkit/build-optimizer: 0.0.32
@angular-devkit/core: 0.0.20
@angular-devkit/schematics: 0.0.35
@ngtools/json-schema: 1.1.0
@ngtools/webpack: 1.8.0
@schematics/angular: 0.1.0
typescript: 2.4.2
webpack: 3.8.1

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 9
  • Comments: 16 (7 by maintainers)

Commits related to this issue

Most upvoted comments

Any tips on how to integrate this fix into an @angular/cli@1.5.0 project without having to ng eject?

https://github.com/typestack/class-transformer/issues/108

@rmrevin

export function serializeType<T>(object: T) {
  return function () { return object; }
}

export class CatalogItem {

  id: string;

  @Type(serializeType(CatalogCategory))
  category?: CatalogCategory = null;

  @Type(serializeType(PackingVariant))
  packing: PackingVariant;

  price: string = null;

}

create a local ng executable that acts as a pass-through. It will run the workaround first, then pass control to the original ng script.

Assuming the following file is in the root of the project:

Filename: ng

#!/usr/bin/env node

/// WORKAROUND HERE

require('node_modules/.bin/ng');

Did not test it, but should work. Maybe some modifications based on OS.

You can also add it to your package.json scripts

OK, for now, if by any change someone is interested, this is a working, monkey patching, workaround:

/**
 * This module will patch the `@angular/compiler-cli` so it will correctly lower expression to declarations in decorators.
 * See https://github.com/angular/angular/issues/20216
 */
import * as ts from 'typescript';
import '@angular/compiler-cli';
const lowerExpressions = require('@angular/compiler-cli/src/transformers/lower_expressions');

function touchNode(node: ts.Node) {
  if (!node.parent) {
    const original: ts.Node = <any> ts.getOriginalNode(node);
    if (original !== node && original.parent) {
      node.parent = original.parent;
      ts.forEachChild(node, touchNode)
    }
  }
}

const getExpressionLoweringTransformFactory = lowerExpressions.getExpressionLoweringTransformFactory;
lowerExpressions.getExpressionLoweringTransformFactory = function(requestsMap, program) {
  const fn = getExpressionLoweringTransformFactory(requestsMap, program);
  return context => sourceFile => {
    const result = fn(context)(sourceFile);
    if (result !== sourceFile) {
      ts.forEachChild(result, touchNode)
    }
    return result;
  };
};

just import this file in the top of your webpack config module, or if you use ngc invoke it from a local file.

Another update to help identify the source:

export class MyClass {
  @MyPropDecorator(() => 15)
  prop: string;
}

It doesn’t have to be a custom type, even intrinsic types will fail.