Typescript: Use before declaration false positive when combined with binding exports

Created on 16 Mar 2017  路  10Comments  路  Source: microsoft/TypeScript

TypeScript Version: 2.2.1

Code

import methodFactory from './http-client'

export { httpDelete as delete } // block scoped httpDelete used before declaration
const httpDelete = methodFactory('DELETE')

Expected behavior:

No error. The export declaration makes no use of the variable; what should happen is that httpDelete's TDZ should extend to the delete export, but that happens regardless of the relative position of the export declaration.

Actual behavior:

Two errors in the same line about the variable being used before assignment.

This error is correct in the case of export default and the typescript-specific export =, but not in any other use of export.

Bug Fixed

Most helpful comment

@Kovensky that's a great point, and I imagine it's one of the reasons that the register format uses var even one targeting es2015. However when TypeScript transpiles to CommonJS, under the es2015 target, it neither uses var nor generates a getter. It just generates an assignment to the exports object. As you say this behavior is not correct. Several issues have been logged, #12522 and #13245 among others, regarding the current behavior.

Thank you for correcting me and for linking to the spec.

All 10 comments

It is use before declaration. The error is valid. The export declaration does make use of the variable as there is an expected assignment. You are making assumptions about how the code is loaded in a loader. While in practice currently, a module loader may not run into a TDZ issue, in practice, the block scoped variable is being used before it is assigned, which is against the language spec.

Indeed, note that there is no error when using var, and that this will work correctly when using a target module format and loader that properly support live bindings.

@kitsonk no, that is literally how it is supposed to work according to spec. No evaluation should happen: 1. Return NormalCompletion(empty)..

If any evaluation _does_ happen, it is a bug with your module loader. There is no assignment expected, at all.

If you do need to convert it to commonjs or whatever is the native data structure for your module loader, you will need to either generate a getter, which is required anyway to correctly implement live binding and frozen module shape, or do a delayed assignment.

@aluanhaddad it will work correctly, which is why it should not be a compile error.

Even the code generated by the playground is (more-or-less) correct, but it will raise a compilation error anyway, even with --modules es2015.

Simple enough fix to not issue this error when the parent is an export declaration

@Kovensky that's a great point, and I imagine it's one of the reasons that the register format uses var even one targeting es2015. However when TypeScript transpiles to CommonJS, under the es2015 target, it neither uses var nor generates a getter. It just generates an assignment to the exports object. As you say this behavior is not correct. Several issues have been logged, #12522 and #13245 among others, regarding the current behavior.

Thank you for correcting me and for linking to the spec.

Does this apply to classes?

The current nightly ([email protected]) raises an error when exporting classes before their declaration. It's a relatively recent change. Two week ago the following code was valid.

export { XYZ }
//       ~~~
// XYZ.ts(1,10): error TS2449: Class 'XYZ' used before its declaration.

class XYZ {
}

@gcnew that should be valid too. Almost certainly the same root cause.

Should be an easy PR if anyone's interested

I haven't tried it, but it seems that @Kovensky's PR (https://github.com/Microsoft/TypeScript/pull/14700) takes care for classes as well. I just saw that he has commented that class exports were miraculously working prior to his change - https://github.com/Microsoft/TypeScript/pull/14700#discussion_r106568485.

Unfortunately, the issue still present if you compile with --strict, otherwise it works fine :).

Was this page helpful?
0 / 5 - 0 ratings