Skip to content

wrong scope for async class member #6549

Closed

Description

@beenotung

Bug Report or Feature Request (mark with an x)

- [x] bug report -> please search issues before submitting
- [ ] feature request

Versions

Output of ng --version:

@angular/cli: 1.0.6
node: 7.10.0
os: linux x64
@angular/animations: 4.1.3
@angular/common: 4.1.3
@angular/compiler: 4.1.3
@angular/core: 4.1.3
@angular/forms: 4.1.3
@angular/http: 4.1.3
@angular/platform-browser: 4.1.3
@angular/platform-browser-dynamic: 4.1.3
@angular/router: 4.1.3
@angular/cli: 1.0.6
@angular/compiler-cli: 4.1.3

Operation System: Archlinux x64

Output of uname -a:

Linux localhost 4.11.2-1-ARCH #1 SMP PREEMPT Mon May 22 06:53:49 CEST 2017 x86_64 GNU/Linux

Repro steps.

  1. create a project from seed ng new name
  2. config the tsconfig.json at project root
{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist/out-tsc",
    "baseUrl": "src",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "allowJs": true,
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2015.promise",
      "es2016",
      "dom"
    ]
  }
}
  1. create and use a class with async method
export class Foo {
  async well(){
    return this.checkLogin();
  }
  async checkLogin() {
    return 'bar';
  }
}
new Foo.well()
  1. compile and serve the project ng build (no compile-time error is generated)
  2. view the website, see the console (run-time TypeError of "Cannot read property 'bar' of undefined" is generated)

The log given by the failure.

core.es5.js:1084 ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'checkLogin' of undefined
TypeError: Cannot read property 'checkLogin' of undefined
    at http://xxx/main.bundle.js:5732:54
    at step (http://xxx/main.bundle.js:5648:23)
    at Object.next (http://xxx/main.bundle.js:5629:53)
    at http://xxx/main.bundle.js:5623:71
    at new ZoneAwarePromise (http://xxx/polyfills.bundle.js:2856:29)
    at __awaiter (http://xxx/main.bundle.js:5619:12)
    at DocumentCenterService.uploadDocument (http://xxx/main.bundle.js:5728:16)
    at Array.map (native)
    at http://xxx/main.bundle.js:1121:50
    at step (http://xxx/main.bundle.js:1039:23)
    at resolvePromise (http://xxx/polyfills.bundle.js:2792:31)
    at http://xxx/polyfills.bundle.js:2718:17
    at rejected (http://xxx/main.bundle.js:10001:89)
    at ZoneDelegate.webpackJsonp.633.ZoneDelegate.invoke (http://xxx/polyfills.bundle.js:2414:26)
    at Object.onInvoke (http://xxx/vendor.bundle.js:8830:37)
    at ZoneDelegate.webpackJsonp.633.ZoneDelegate.invoke (http://xxx/polyfills.bundle.js:2413:32)
    at Zone.webpackJsonp.633.Zone.run (http://xxx/polyfills.bundle.js:2164:43)
    at http://xxx/polyfills.bundle.js:2840:57
    at ZoneDelegate.webpackJsonp.633.ZoneDelegate.invokeTask (http://xxx/polyfills.bundle.js:2447:31)
    at Object.onInvokeTask (http://xxx/vendor.bundle.js:8821:37)

It seems to fail because of the this is not the owner of the prototype but function passed to the generator.

Desired functionality.

The compiler shall use a variable to reference the scope and replace the usage of 'this'. An example of generated code:

DocumentCenterService.prototype.uploadDocument = function (file) {
        var _this = this;
        return __awaiter(this, void 0, void 0, function () {
            var desc, res, docID, formData, res2, fileID, uploadFile;
            return __generator(_this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, _this.checkLogin()];
                    

It should return a promise of 'bar' in above example without error.

Mention any other details that might be useful.

Sample of output bundled js

Notice there is a function at line 2 (instead of arrow function) and the this at line 6 is not escaped.

DocumentCenterService.prototype.uploadDocument = function (file) {
        return __awaiter(this, void 0, void 0, function () {
            var desc, res, docID, formData, res2, fileID, uploadFile;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.checkLogin()];
                    case 1:
                        _a.sent();
                        desc = new __WEBPACK_IMPORTED_MODULE_9__model_api_documentFileOverview__["a" /* model */].api.DocumentFileDescription(file);
            

Activity

  1. beenotung commented on Jun 2, 2017

    @beenotung
    Author

    I am not sure to fire this issue to angular, webpack, of typescript repo. Please correct me if I post it in the wrong place.

  2. clydin commented on Jun 2, 2017

    @clydin
    Member

    Have you tried using version 1.1 of the CLI? It fully supports Typescript 2.3.
    Also, you shouldn't have to make any changes to tsconfig.

  3. sumitarora commented on Jun 6, 2017

    @sumitarora
    Contributor

    @beenotung You have to initalize the class before you can make use of it. Changing new Foo.well() to new Foo().well() worked fine for me.

  4. beenotung commented on Jun 18, 2017

    @beenotung
    Author

    @sumitarora that is typo in the example, it's called correctly in the actual code base

  5. beenotung commented on Jun 18, 2017

    @beenotung
    Author

    It seems due to the leaky class method.
    In the real code, I used Promise.all and Array.prototype.map on the async method.

    Example:

    class Animal {
      name: string;
      constructor(name){
        this.name = name;
      }
      speak(x){
        return `Hi ${x}, I am ${this.name}`;
      }
    }
    
    const dog = new Animal('dog');
    const xs = [1, 2];
    const res = xs.map(dog.speak);

    The first guess of res:

    ["Hi 1, I'm dog", "Hi 2, I'm dog"]

    The real value of res:

    ["Hi 1, I'm undefined", "Hi 2, I'm undefined"]

    The 'correct' ways:

    xs.map(dog.speak.bind(dog))
    xs.map(x => dog.speak(x))

    So it seems not related to any compilers, this issue can be closed.

  6. angular-automatic-lock-bot commented on Sep 7, 2019

    @angular-automatic-lock-bot

    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.

  7. locked and limited conversation to collaborators on Sep 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

needs: investigationRequires some digging to determine if action is needed

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions