Skip to content
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

Duplicate identifier 'Promise' (TS2529) too restrictive. #12323

Closed
electricessence opened this issue Nov 17, 2016 · 9 comments
Closed

Duplicate identifier 'Promise' (TS2529) too restrictive. #12323

electricessence opened this issue Nov 17, 2016 · 9 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@electricessence
Copy link

electricessence commented Nov 17, 2016

Error:(8, 9) TS2529:Duplicate identifier 'Promise'. Compiler reserves name 'Promise' in top level scope of a module containing async functions.

TypeScript Version: 2.1.1 (targeting ES5)
Code

import {Promise} from "myOwnPromiseLib";

What I'm confused about is when I look at the __awaiter code it expects Promise (P) to be available.
In a modular environment, how else am I supposed to inject my own Promise lib so it can use it?

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments)).next());
    });
};
@electricessence
Copy link
Author

For my current case, I'm able to get this working by:

export interface Awaiter
{
    (thisArg:any, _arguments:any[], P:PromiseConstructorLike, generator:Function):void;
}

export function awaiter(
    thisArg:any,
    _arguments:any[],
    P:PromiseConstructorLike,
    generator:Function)
{
    if(!P) throw "Must provide Promise constructor.  Try injecting Promise using awaiter.factory(PromiseConstructorLike).";
    return new P((resolve, reject) =>
    {
        const g = generator = generator.apply(thisArg, _arguments);
        step(g.next());

        function fulfilled(value:any)
        {
            try
            { step(g.next(value)); }
            catch(e)
            { reject(e); }
        }

        function rejected(value:any)
        {
            try
            { step(g["throw"](value)); }
            catch(e)
            { reject(e); }
        }

        function step(result:any)
        {
            result.done
                ? resolve(result.value)
                : new P(resolve => { resolve(result.value); }).then(fulfilled, rejected);
        }
    });
}

export module awaiter
{
    export function factory(Promise:PromiseConstructorLike):Awaiter
    {
        return (thisArg:any, _arguments:any[], P:PromiseConstructorLike, generator:Function) =>
        {
            awaiter(thisArg, _arguments, P || Promise, generator);
        };
    }
}

@yortus
Copy link
Contributor

yortus commented Nov 17, 2016

For background on why this was done, see #6068 and #6631. I personally agree it's too restrictive but those links explain the justification for the current behaviour.

@electricessence
Copy link
Author

@yortus Ok that makes sense. And apparently this may change once generators are supported in es5?
But what I'm really confused about for es5, is (because I've been living in module land for so long) how to I specify Promise as a global so it can be consumed by the default __awaiter function?

Ok, why not simply DI it like what I pasted above? I know what I have here isn't perfect, but it's working for me so it's possible that each module can decide it's own promise lib.

@yortus
Copy link
Contributor

yortus commented Nov 18, 2016

how to I specify Promise as a global so it can be consumed by the default __awaiter function?

There are two ways:

1. Polyfill Promise

  1. use a Promise polyfill so Promise is globally defined at runtime (e.g. es6-promise or similar)
  2. Ensure the TypeScript project includes ambient Promise typings, (e.g. use option "lib": ["es2015.promise"])

2. Annotate all your async functions

e.g.,

// Only works with target <= ES5
import BluebirdPromise = require('bluebird');
async function myFunc(): BluebirdPromise {
    await someOp();
}

The type annotation BluebirdPromise will be emitted into a value position in the .js file and passed to __awaiter, so your async function will use and return a bluebird promise in this case.

I'm not a fan of the second approach for reasons listed here.

@mhegazy mhegazy added the Working as Intended The behavior described is the intended behavior; this is not a bug label Nov 18, 2016
@electricessence
Copy link
Author

Ok, now I'm more interested how I can simply keep using my own helpers and not require a global reference.

I've got this working the way I want at the moment, but now I'm getting:

Error:TS2318:Cannot find global type 'Promise'.

@yortus
Copy link
Contributor

yortus commented Nov 18, 2016

Did you try "lib": ["es2015.promise"] in your tsconfig?

@electricessence
Copy link
Author

I could do that but I'm not using es2015.promise. Does it end up just being a hack so I can ignore?

@yortus
Copy link
Contributor

yortus commented Nov 18, 2016

Are you polyfilling Promise at runtime so it's globally available as Promise? If so, the lib option will just tell the compiler that a global Promise exists and prevent the TS2318 error, and it should both compile and run fine.

If you are not using a global polyfill but importing some promise implementation locally, then I think you must either:

  • a) use the annotation approach to avoid compiler errors, even if you provide your own __awaiter. -or-
  • b) use the lib option to prevent TS2318 but then ensure your custom __awaiter ignores it's third argument (the P argument). This is a bit hacky because you are telling the compiler a global Promise exists when it doesn't. It might cause runtime errors elsewhere in your code if it references this phantom global Promise.

@electricessence
Copy link
Author

Well now that I've updated to 2.2 dev, it's gotten even weirder:
#12374

@mhegazy mhegazy closed this as completed Feb 28, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants