-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Closed
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issueHas ReproThis issue has compiler-backed repros: https://aka.ms/ts-reprosThis issue has compiler-backed repros: https://aka.ms/ts-reprosRescheduledThis issue was previously scheduled to an earlier milestoneThis issue was previously scheduled to an earlier milestone
Milestone
Description
TypeScript Version: 3.8.3
Search Terms: computed object property name destructuring private ECMAScript
Code
class PropertyAccessor {
readonly #propertyName: string;
constructor(propertyName: string) {
this.#propertyName = propertyName;
}
getPropertyValue(obj: Record<string, string | undefined>): string | undefined {
const { [this.#propertyName]: value } = obj;
return value;
}
}
const accessor = new PropertyAccessor('name');
console.log(
accessor.getPropertyValue({ name: 'Adam' })
);
Expected behavior:
'Adam' is logged at runtime.
Actual behavior:
Runtime error occurs:
Private field '#propertyName' must be declared in an enclosing class
Additional information:
I just started using TypeScript and I ran into this problem while converting one of my javascript projects, the behavior was confusing to me. It seems like the code is transpiled incorrectly, as it works when not using destructuring syntax:
const { [this.#propertyName]: value } = obj;
// compiles to
const { [(_propertyName = new WeakMap(), this.#propertyName)]: value } = obj;
const value = obj[this.#propertyName];
// compiles to
const value = obj[__classPrivateFieldGet(this, _propertyName)];
The error does not occur when using TypeScript's private
field instead of ECMAScript's.
The error does not occur when targeting ESNext.
Related Issues: Maybe #26572 #26355? Doesn't seem like the same problem but similar keywords.
Output
"use strict";
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to set private field on non-instance");
}
privateMap.set(receiver, value);
return value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to get private field on non-instance");
}
return privateMap.get(receiver);
};
var _propertyName;
class PropertyAccessor {
constructor(propertyName) {
_propertyName.set(this, void 0);
__classPrivateFieldSet(this, _propertyName, propertyName);
}
getPropertyValue(obj) {
const { [(_propertyName = new WeakMap(), this.#propertyName)]: value } = obj;
return value;
}
}
const accessor = new PropertyAccessor('name');
console.log(accessor.getPropertyValue({ name: 'Adam' }));
Compiler Options
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"alwaysStrict": true,
"esModuleInterop": true,
"declaration": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": 2,
"target": "ES2017",
"jsx": "React",
"module": "ESNext"
}
}
Playground Link: Provided
Metadata
Metadata
Assignees
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issueHas ReproThis issue has compiler-backed repros: https://aka.ms/ts-reprosThis issue has compiler-backed repros: https://aka.ms/ts-reprosRescheduledThis issue was previously scheduled to an earlier milestoneThis issue was previously scheduled to an earlier milestone