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

Some ES6 constructs force an implicit 'strict' context within them. #1135

Closed
CyrusNajmabadi opened this issue Nov 12, 2014 · 10 comments
Closed
Assignees
Labels
Breaking Change Would introduce errors in existing code Bug A bug in TypeScript ES6 Relates to the ES6 Spec

Comments

@CyrusNajmabadi
Copy link
Contributor

For example, inside the body of a class, you are implicitly in strict mode. Implementing this will be a breaking change for existing code that uses classes today and uses some identifier (like 'yield') that becomes a keyword in strict mode.

@CyrusNajmabadi CyrusNajmabadi added the Breaking Change Would introduce errors in existing code label Nov 12, 2014
@CyrusNajmabadi
Copy link
Contributor Author

Features that enable strict mode:

Module Declarations
Class Declarations
Class Expressions
(still reading the spec to see if there is more).

@danquirk danquirk added the Bug A bug in TypeScript label Nov 12, 2014
@mhegazy
Copy link
Contributor

mhegazy commented Nov 13, 2014

Just as a note. It does not have to be a breaking change. The rule can only be enforced when you are targeting es6. This should only affect users moving to es6.

@CyrusNajmabadi
Copy link
Contributor Author

@mhegazy That's still a breaking change :) Having your code break when moving forward means that TypeScript+ES6 is breaking over TypeScript+ES5.

@mhegazy
Copy link
Contributor

mhegazy commented Nov 13, 2014

Not really. Octal literals is a similar example.

@DanielRosenwasser
Copy link
Member

I think when we generally say "breaking change", we mean it to say that

  1. if a user has a codebase that compiles with some set of options [opts] and
  2. downloads a new version of the compiler and
  3. runs the new compiler on that codebase with [opts] and
  4. finds that [opts] is not available or that the new compiler errors on their codebase

then that's a breaking change. We never shipped ES6 mode so I don't see this as a breaking change.

Still, we should document this explicitly irrespective of the semantics of what constitutes a breaking change (which I could not care less about (except just enough to give a list-based predicate defining it in this response)).

@CyrusNajmabadi
Copy link
Contributor Author

Those are not the semantics that DevDiv have ever followed when deciding if something is a breaking change. Breaking changes are breaking changes if you move forward (including through language/program version options) and your code no longer compiles. You'll note that C# has taken great pains to avoid this sort of issue (though though they even occasionally makes breaks). For example, there was a decision made to require you to use yield return x instead of yield x in an iterator because of hte break that would happen if you had a type called 'yield'. This was the case even though they could have said "if you picked a different 'langversion' that it's ok for us to break your code'.

C# clearly avoided this (though your above approach was an option), and this was because they wanted to make sure your existing code could move forward without breaking to new versions of the language. ***

Note also: ScriptTarget affects the code we are generating. i.e. if we're going to emit ES3, ES5, or ES6. It does not affect our actual understanding of your code. You are getting the latest version of our language no matter what the final script target option is.

An important reason for this is we want to ensure that any code you have means the same thing, regardless what version of the compiler you have. i.e. once you have a version of the compiler that supports that feature, then the interpretation of that feature should not change. As we have supported classes since V1, it's a break if we change our interpretation of what a class is later. Now, personally, i think it's likely fine for us to make that break. I'm just being explicit that this is what it is so we can document things properly as per our requirements.

*** Note: C# has made explicit breaking changes when it deemed it valuable enough. For example, if you had Foo(Bar<X,Y>(10)) then a breaking change was explicitly made to always parse that (no matter what language version you specify) as a generic call with type type arguments, rather than two arithmetic expressions. Occasionally breaks are simply worth it. You just need do evaluate if that's the case, make the decision, and document it.

@CyrusNajmabadi
Copy link
Contributor Author

Note: to clarify: ScriptTarget affects your final JavaScript. i.e. should an ArrowFunction get rewritten to a FunctionExpression, or should it stay as an ArrowFunction. (Same with things like Templates/Classes/Modules/etc.). ScriptTarget does not change our interpretation of the code. Nor should it. That's why we call it 'script target'. It is the option to specify what sort of 'script' you are 'target'ing :)

We only use the script target for two purposes.

  1. To let the user know if they're using a construct that we don't support emitting for. For example, we don't have a way to emit 'let' statements in ES5 or lower. So we error on you trying to use those constructs on anything less than ES6.

  2. To customize how we actually generate your final javascript. In general, the higher the version, the less mutation we'll do on your code (as we can use a better javascript construct that is available at that version).

We have never gone down the route of saying that we will change the semantics of hte language between script versions except for where ECMAScript has made that decisions themselves.

@billti
Copy link
Member

billti commented Nov 13, 2014

I think technically Cyrus is right. Currently, unless you specify "use strict;" in your code (impressed we got this right ), you can use identifiers such as "private" and "interface" inside classes and modules. If this is going to be invalid code in ES6, then we can't emit it when targeting ES6, it has to be an error.

I expect the code this will break will be minimal, as strict mode only really impacts TypeScript in the area of these future reserved keywords, and very little code uses identifier names such as "yield" or "static". If it only occurs when targeting ES6 output, it seems palatable to need to fix a couple of identifier names while you are changing your compiler switches. We probably don't want to make this an error in non-ES6 targeted output as a) Builds will break just from an upgrade of the compiler, and b) It is valid code in <= ES5 output.

@billti billti added this to the TypeScript 2.0 milestone Nov 21, 2014
@billti billti added the ES6 Relates to the ES6 Spec label Nov 21, 2014
@mhegazy mhegazy modified the milestones: TypeScript 1.5, TypeScript 2.0 Apr 2, 2015
@mhegazy mhegazy assigned yuit and unassigned CyrusNajmabadi Apr 2, 2015
@yuit
Copy link
Contributor

yuit commented May 4, 2015

ES6 with Implicit Strict Mode Context

  • Class Declaration
  • Class Expression
  • Module Declaration

@yuit
Copy link
Contributor

yuit commented Jun 30, 2015

PR#3517 covered parsing module declaration in strict mode

@yuit yuit closed this as completed Jun 30, 2015
@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Breaking Change Would introduce errors in existing code Bug A bug in TypeScript ES6 Relates to the ES6 Spec
Projects
None yet
Development

No branches or pull requests

6 participants