You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ES5 class rewriting incompatible with frozen intrinsics #43450
When TSC targets ES5, classes that extend other classes generate JS that throws a Cannot assign to read only property 'constructor' of object TypeError when the intrinsics, particularly the Function.prototype, are frozen. This affects programs that are freezing their intrinsics to protect against supply chain attacks, particularly using SES.
TypeError: Cannot assign to read only property 'constructor' of object ''
🙂 Expected behavior
This should not throw an exception. This can be achieved by using Object.defineProperty(prototype, 'constructor', {value: constructor}) instead of prototype.constructor = constructor. This is a symptom of the so-called property override mistake, in the design of ES5.
This can be achieved by using Object.defineProperty(prototype, 'constructor', {value: constructor})
Our helper emit is designed to work in ES3 as well, where defineProperty is not available. I'm not sure that other alternatives are available; you can provide your own __extends helper that uses that approach if you're not running in ES3 environments.
There’s no way to discriminate the output based on ES5 vs ES3?
This just confuses the situation, because this is a global helper with "first in wins" semantics (to enable you to provide your own, as mentioned) - so if an ES3-targeted library loads before an ES5-targeted library, which is a totally legal situation, then the ES3 version of the helper is going to win and you're back where you started.
We do that in __makeTemplateObject, __createBinding, and __setModuleDefault for what it's worth.
I took a look and we do that wrong in __makeTemplateObject, unfortunately. For __createBinding and __setModuleDefault we check for Object.create (which is good), but we check for Object.defineProperty in __makeTemplateObject. The problem with testing for Object.defineProperty is that IE8's Object.defineProperty could only be used on DOM nodes, however IE8 did not support Object.create.
So, we need to fix __makeTemplateObject, and the __extends helper should probably be:
var__extends=(this&&this.__extends)||(function(){varextendStatics=function(d,b){extendStatics=Object.setPrototypeOf||({__proto__: []}instanceofArray&&function(d,b){d.__proto__=b;})||function(d,b){for(varpinb)if(Object.prototype.hasOwnProperty.call(b,p))d[p]=b[p];};returnextendStatics(d,b);};varsetConstructor=function(f,d){setConstructor=Object.create ?
function(f,d){Object.defineProperty(f,"constructor",{value: d,writable: true,configurable: true});} :
function(f,d){f.constructor=d;};setConstructor(f,d);};returnfunction(d,b){if(typeofb!=="function"&&b!==null)thrownewTypeError("Class extends value "+String(b)+" is not a constructor or null");extendStatics(d,b);function__(){setConstructor(this,d);}d.prototype=b===null ? Object.create(b) : (__.prototype=b.prototype,new__());};})();
I offered a fix at #62397, but it was closed because this issue is not open for PRs, with the implication that accepting it could impose friction for anyone relying on the incorrect behavior (which, for the record, is failure to evaluate the definition of a class that uses extends) and the explanation that user-side workarounds are "fairly straightforward" ("inject your own __extends or use a different downleveler").
In my response, I question that anyone could be depending upon the incorrect behavior, and further challenge availability of the workarounds when this issue manifests inside a dependency, as in the case of both the initial report here (external-editor version 3.1.0) and the encounter that prompted me to open the PR (json-rpc-2.0 version 1.7.1)—nor would it be reasonable IMO to expect every JS-importable library written in TypeScript to customize downleveling of extends.
What would it take to move this issue along such that #62397 could be reopened and accepted?
Activity
erights commentedon Mar 30, 2021
Attn Daniel Rosenwasser (@DanielRosenwasser) Ron Buckton (@rbuckton)
RyanCavanaugh commentedon Mar 30, 2021
Our helper emit is designed to work in ES3 as well, where
definePropertyis not available. I'm not sure that other alternatives are available; you can provide your own__extendshelper that uses that approach if you're not running in ES3 environments.kriskowal commentedon Mar 30, 2021
There’s no way to discriminate the output based on ES5 vs ES3?
kriskowal commentedon Mar 30, 2021
Would it be an option to feature-test Object.defineProperty to discriminate at runtime?
RyanCavanaugh commentedon Mar 30, 2021
This just confuses the situation, because this is a global helper with "first in wins" semantics (to enable you to provide your own, as mentioned) - so if an ES3-targeted library loads before an ES5-targeted library, which is a totally legal situation, then the ES3 version of the helper is going to win and you're back where you started.
kriskowal commentedon Mar 31, 2021
Would it work if the ES3 and ES5 helpers were identical:
DanielRosenwasser commentedon Mar 31, 2021
We do that in
__makeTemplateObject,__createBinding, and__setModuleDefaultfor what it's worth.rbuckton commentedon Apr 1, 2021
I took a look and we do that wrong in
__makeTemplateObject, unfortunately. For__createBindingand__setModuleDefaultwe check forObject.create(which is good), but we check forObject.definePropertyin__makeTemplateObject. The problem with testing forObject.definePropertyis that IE8'sObject.definePropertycould only be used on DOM nodes, however IE8 did not supportObject.create.So, we need to fix
__makeTemplateObject, and the__extendshelper should probably be:kumavis commentedon Apr 12, 2021
Ron Buckton (@rbuckton) should this
be
kumavis commentedon Apr 12, 2021
oh i see you explained exactly why you did this 🤦 I suppose adding a comment would help clarify its intentional. 😸
erights commentedon Jun 9, 2021
Hi Ron Buckton (@rbuckton) any progress on #43450 (comment) ?
Support downleveling classes that extend frozen bases
gibson042 commentedon Sep 9, 2025
I offered a fix at #62397, but it was closed because this issue is not open for PRs, with the implication that accepting it could impose friction for anyone relying on the incorrect behavior (which, for the record, is failure to evaluate the definition of a class that uses
extends) and the explanation that user-side workarounds are "fairly straightforward" ("inject your own__extendsor use a different downleveler").In my response, I question that anyone could be depending upon the incorrect behavior, and further challenge availability of the workarounds when this issue manifests inside a dependency, as in the case of both the initial report here (external-editor version 3.1.0) and the encounter that prompted me to open the PR (json-rpc-2.0 version 1.7.1)—nor would it be reasonable IMO to expect every JS-importable library written in TypeScript to customize downleveling of
extends.What would it take to move this issue along such that #62397 could be reopened and accepted?
RyanCavanaugh commentedon Sep 9, 2025
ES5 is being deprecated, so no further changes are warranted here.