TypeScript Version: 3.6.3
Code
This works:
/** @enum {number} */
const MyEnum = {
a: 1,
b: 2
};
/** @type {MyEnum} */
const v = MyEnum.b;
This doesn't work (MyEnum imported from separate file):
// my-enum.js
/** @enum {number} */
const MyEnum = {
a: 1,
b: 2
};
export default MyEnum;
// main.js
import MyEnum from './my-enum.js';
/** @type {MyEnum} */ // ERROR: 'MyEnum' refers to a value, but is being used as a type here. ts(2749)
const v = MyEnum.b;
Expected behavior:
No type errors.
It did work fine in v2.8.3
I have the inverse of this issue, where I can't do something like:
import {MyEnum} from './my-enum.js';
const foo = MyEnum.a; // "'MyEnum' only refers to a type, but is being used as a value here"
const bar = MyEnum['b']; // Same thing
switch(baz) {
case MyEnum.a: // and also here
console.log("xyz");
break;
case MyEnum.b: //and here
break;
default:
}
It used to work as of TypeScript 3.5.3, I discovered this issue when upgrading a project this morning.
I can confirm that 3.5.3 was the last version where @art-in's scenario worked. We are having the same issue in OpenLayers.
What still works is this:
// main.js
import MyEnum from './my-enum.js';
/** @type {import("./my-enum.js").default} */ // No error
const v = MyEnum.b;
But that's a bit cumbersome, considering that we have MyEnum imported already.
Given that 3.7 has the game-changing #7546, I would be very sad to not be able to use it because of this.
MyEnum resolves to { [s: string]: number } not number as it should be.The problem is that the imported MyEnum is marked as BlockScopedVariable, not TypeAlias. So it incorrectly falls through the type alias case and uses the jsdoc code path that treats values as types. This is not the right code path (I think) even though it now returns a type.
I think the fix is to resolve the alias symbol for MyEnum correctly.
I can confirm that #34515 does not fix the issue. The error is now
Argument of type 'number' is not assignable to parameter of type '{ a: number; b: number; }
@sandersn Is there anything I can help with to get this resovled?
I believe @elibarzilay is looking at the alias resolution code right now in order to fix the symbol's flags. I hope it's an overlooked code path and not something deep in alias resolution.
I just tried v3.8.1-rc, but this is still broken. Has there been any progress?
Thanks everyone for the great work recently on JSDoc support, which allows us to publish auto-generated .d.ts files from our JSDoc annotated JavaScript code base. That's awesome!
This one is the last blocker for TypeScript being 100% useful with JSDoc annotations. Is there a new milestone for this? It is still broken in v3.9.0-dev.
The problem boils down to this:
Assume you have the following my-enum.js file:
/** @enum {number} */
const MyEnum = {
a: 1,
b: 2
};
export default MyEnum;
Now when you import the enum in a different file, the following will work:
import MyEnum from './my-enum.js';
/** @type {import("./my-enum.js").default} */
const v = MyEnum.b;
The follwoing will cause the reported error:
import MyEnum from './my-enum.js';
/** @type {MyEnum} */
const v = MyEnum.b;
I believe this is because MyEnum is also used as an object literal here, so the type is resolved differently. The above is equivalent to this, which triggers the same error:
import MyEnum from './my-enum.js';
/** @type {typeof MyEnum} */
const v = MyEnum.b;
Resummarizing this bug, you cannot import a value marked with @enum. This regressed in TypeScript 3.6.
/** @enum {string} */
const x = { a: 'b' };
export {x};
// new file:
import {x} from './otherfile.js';
x.a; // ERROR
workaround is to write export const instead of the separate export.
Quick note: the /** @type {import("./my-enum.js").default} */ case is broken too -- it gets a type of any.
And what @elibarzilay mentions also happens when the workaround mentioned in https://github.com/microsoft/TypeScript/issues/33575#issuecomment-624350386 is used.
@sandersn: I just tried #39770, and it doesn't fix this issue or the any that I mentioned above.
Quick note虏: The /** @type {import("./my-enum.js").default} */ thing is working with the fix too.
Most helpful comment
The problem boils down to this:
Assume you have the following
my-enum.jsfile:Now when you import the enum in a different file, the following will work:
The follwoing will cause the reported error:
I believe this is because
MyEnumis also used as an object literal here, so the type is resolved differently. The above is equivalent to this, which triggers the same error: