Skip to content

wrong scope for async class member #6549

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
beenotung opened this issue Jun 2, 2017 · 6 comments
Closed

wrong scope for async class member #6549

beenotung opened this issue Jun 2, 2017 · 6 comments
Assignees
Labels
needs: investigation Requires some digging to determine if action is needed

Comments

@beenotung
Copy link

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);
            
@beenotung
Copy link
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.

@clydin
Copy link
Member

clydin commented Jun 2, 2017

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.

@filipesilva filipesilva added the needs: investigation Requires some digging to determine if action is needed label Jun 6, 2017
@sumitarora
Copy link
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.

@beenotung
Copy link
Author

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

@beenotung
Copy link
Author

beenotung commented Jun 18, 2017

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.

@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 Sep 7, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
needs: investigation Requires some digging to determine if action is needed
Projects
None yet
Development

No branches or pull requests

4 participants