'this' is Undefined in TypeScript Property Decorator
tl;dr: I'm not sure why OP's config didn't work; it seems to work beautifully now. See below for some brute-force testing.
Guess
I'm wondering if you were somehow picking up the wrong tsconfig
. I've looked at your repo's tsconfig
and it looks correct. Is there any chance another config file was infecting those runs? I see there were no automated tests there.
Testing
I ran into a similar issue today and threw together a quick test using OP as a blueprint. I pulled compiler options from the official docs.
decorators.ts
export function logProperty(target: any, key: string) { let val = this[key]; const getter = () => { console.log(`Get: ${key} => ${val}`); return val; }; const setter = (newVal) => { console.log(`Set: ${key} => ${newVal}`); val = newVal; }; if (delete this[key]) { Object.defineProperty(target, key, { get: getter, set: setter, enumerable: true, configurable: true }); }}
main.ts
import { logProperty } from './decorators';class Person { @logProperty firstName: string; @logProperty lastName: string; constructor(firstName: string, lastName: string) { this.firstName = firstName; this.lastName = lastName; }}const foo = new Person('Foo', 'Bar');function logProperty2(target: any, key: string) { let val = this[key]; const getter = () => { console.log(`Get: ${key} => ${val}`); return val; }; const setter = (newVal) => { console.log(`Set: ${key} => ${newVal}`); val = newVal; }; if (delete this[key]) { Object.defineProperty(target, key, { get: getter, set: setter, enumerable: true, configurable: true }); }}class Person2 { @logProperty2 firstName: string; @logProperty2 lastName: string; constructor(firstName: string, lastName: string) { this.firstName = firstName; this.lastName = lastName; }}const foo2 = new Person2('Foo', 'Bar');
index.ts
import * as assert from "assert";import * as shelljs from "shelljs";const MODULE_GENERATION = [ "CommonJS", "AMD", "System", "UMD", "ES6", "ES2015", "ESNext",];const TARGETS = [ "ES5", "ES2015", "ES2016", "ES2017"]shelljs.exec("tsc --target 'ES5' --module 'None' --strict main.ts", { silent: true });assert.ok(shelljs.error());shelljs.exec("tsc --target 'ES5' --module 'None' main.ts", { silent: true });assert.ok(shelljs.error());for (const moduleGeneration of MODULE_GENERATION) { console.log(`Testing module generation: ${moduleGeneration}`); for (const target of TARGETS) { console.log(` Building for ${target}`); for (const strict of [true, false]) { console.log(` Strict mode: ${strict ? 'en' : 'dis'}abled`) const command = ( `tsc` + ` --module '${moduleGeneration}'` + ` --experimentalDecorators` + ` --target '${target}'` + ` ${strict ? "--strict" : ""}` + ` main.ts` ); const output = shelljs.exec( command, { silent: true }, ); let symbol; if (strict) { assert.ok(shelljs.error()); symbol = '✖' } else { assert.strictEqual(0, output.code); symbol = '✓' } console.log(` ${symbol} ${command}`); } }}
Results
You can see the full build on Travis.
Testing module generation: CommonJS Building for ES5 Strict mode: enabled ✖ tsc --module 'CommonJS' --experimentalDecorators --target 'ES5' --strict main.ts Strict mode: disabled ✓ tsc --module 'CommonJS' --experimentalDecorators --target 'ES5' main.ts Building for ES2015 Strict mode: enabled ✖ tsc --module 'CommonJS' --experimentalDecorators --target 'ES2015' --strict main.ts Strict mode: disabled ✓ tsc --module 'CommonJS' --experimentalDecorators --target 'ES2015' main.ts Building for ES2016 Strict mode: enabled ✖ tsc --module 'CommonJS' --experimentalDecorators --target 'ES2016' --strict main.ts Strict mode: disabled ✓ tsc --module 'CommonJS' --experimentalDecorators --target 'ES2016' main.ts Building for ES2017 Strict mode: enabled ✖ tsc --module 'CommonJS' --experimentalDecorators --target 'ES2017' --strict main.ts Strict mode: disabled ✓ tsc --module 'CommonJS' --experimentalDecorators --target 'ES2017' main.tsTesting module generation: AMD Building for ES5 Strict mode: enabled ✖ tsc --module 'AMD' --experimentalDecorators --target 'ES5' --strict main.ts Strict mode: disabled ✓ tsc --module 'AMD' --experimentalDecorators --target 'ES5' main.ts Building for ES2015 Strict mode: enabled ✖ tsc --module 'AMD' --experimentalDecorators --target 'ES2015' --strict main.ts Strict mode: disabled ✓ tsc --module 'AMD' --experimentalDecorators --target 'ES2015' main.ts Building for ES2016 Strict mode: enabled ✖ tsc --module 'AMD' --experimentalDecorators --target 'ES2016' --strict main.ts Strict mode: disabled ✓ tsc --module 'AMD' --experimentalDecorators --target 'ES2016' main.ts Building for ES2017 Strict mode: enabled ✖ tsc --module 'AMD' --experimentalDecorators --target 'ES2017' --strict main.ts Strict mode: disabled ✓ tsc --module 'AMD' --experimentalDecorators --target 'ES2017' main.tsTesting module generation: System Building for ES5 Strict mode: enabled ✖ tsc --module 'System' --experimentalDecorators --target 'ES5' --strict main.ts Strict mode: disabled ✓ tsc --module 'System' --experimentalDecorators --target 'ES5' main.ts Building for ES2015 Strict mode: enabled ✖ tsc --module 'System' --experimentalDecorators --target 'ES2015' --strict main.ts Strict mode: disabled ✓ tsc --module 'System' --experimentalDecorators --target 'ES2015' main.ts Building for ES2016 Strict mode: enabled ✖ tsc --module 'System' --experimentalDecorators --target 'ES2016' --strict main.ts Strict mode: disabled ✓ tsc --module 'System' --experimentalDecorators --target 'ES2016' main.ts Building for ES2017 Strict mode: enabled ✖ tsc --module 'System' --experimentalDecorators --target 'ES2017' --strict main.ts Strict mode: disabled ✓ tsc --module 'System' --experimentalDecorators --target 'ES2017' main.tsTesting module generation: UMD Building for ES5 Strict mode: enabled ✖ tsc --module 'UMD' --experimentalDecorators --target 'ES5' --strict main.ts Strict mode: disabled ✓ tsc --module 'UMD' --experimentalDecorators --target 'ES5' main.ts Building for ES2015 Strict mode: enabled ✖ tsc --module 'UMD' --experimentalDecorators --target 'ES2015' --strict main.ts Strict mode: disabled ✓ tsc --module 'UMD' --experimentalDecorators --target 'ES2015' main.ts Building for ES2016 Strict mode: enabled ✖ tsc --module 'UMD' --experimentalDecorators --target 'ES2016' --strict main.ts Strict mode: disabled ✓ tsc --module 'UMD' --experimentalDecorators --target 'ES2016' main.ts Building for ES2017 Strict mode: enabled ✖ tsc --module 'UMD' --experimentalDecorators --target 'ES2017' --strict main.ts Strict mode: disabled ✓ tsc --module 'UMD' --experimentalDecorators --target 'ES2017' main.tsTesting module generation: ES6 Building for ES5 Strict mode: enabled ✖ tsc --module 'ES6' --experimentalDecorators --target 'ES5' --strict main.ts Strict mode: disabled ✓ tsc --module 'ES6' --experimentalDecorators --target 'ES5' main.ts Building for ES2015 Strict mode: enabled ✖ tsc --module 'ES6' --experimentalDecorators --target 'ES2015' --strict main.ts Strict mode: disabled ✓ tsc --module 'ES6' --experimentalDecorators --target 'ES2015' main.ts Building for ES2016 Strict mode: enabled ✖ tsc --module 'ES6' --experimentalDecorators --target 'ES2016' --strict main.ts Strict mode: disabled ✓ tsc --module 'ES6' --experimentalDecorators --target 'ES2016' main.ts Building for ES2017 Strict mode: enabled ✖ tsc --module 'ES6' --experimentalDecorators --target 'ES2017' --strict main.ts Strict mode: disabled ✓ tsc --module 'ES6' --experimentalDecorators --target 'ES2017' main.tsTesting module generation: ES2015 Building for ES5 Strict mode: enabled ✖ tsc --module 'ES2015' --experimentalDecorators --target 'ES5' --strict main.ts Strict mode: disabled ✓ tsc --module 'ES2015' --experimentalDecorators --target 'ES5' main.ts Building for ES2015 Strict mode: enabled ✖ tsc --module 'ES2015' --experimentalDecorators --target 'ES2015' --strict main.ts Strict mode: disabled ✓ tsc --module 'ES2015' --experimentalDecorators --target 'ES2015' main.ts Building for ES2016 Strict mode: enabled ✖ tsc --module 'ES2015' --experimentalDecorators --target 'ES2016' --strict main.ts Strict mode: disabled ✓ tsc --module 'ES2015' --experimentalDecorators --target 'ES2016' main.ts Building for ES2017 Strict mode: enabled ✖ tsc --module 'ES2015' --experimentalDecorators --target 'ES2017' --strict main.ts Strict mode: disabled ✓ tsc --module 'ES2015' --experimentalDecorators --target 'ES2017' main.tsTesting module generation: ESNext Building for ES5 Strict mode: enabled ✖ tsc --module 'ESNext' --experimentalDecorators --target 'ES5' --strict main.ts Strict mode: disabled ✓ tsc --module 'ESNext' --experimentalDecorators --target 'ES5' main.ts Building for ES2015 Strict mode: enabled ✖ tsc --module 'ESNext' --experimentalDecorators --target 'ES2015' --strict main.ts Strict mode: disabled ✓ tsc --module 'ESNext' --experimentalDecorators --target 'ES2015' main.ts Building for ES2016 Strict mode: enabled ✖ tsc --module 'ESNext' --experimentalDecorators --target 'ES2016' --strict main.ts Strict mode: disabled ✓ tsc --module 'ESNext' --experimentalDecorators --target 'ES2016' main.ts Building for ES2017 Strict mode: enabled ✖ tsc --module 'ESNext' --experimentalDecorators --target 'ES2017' --strict main.ts Strict mode: disabled ✓ tsc --module 'ESNext' --experimentalDecorators --target 'ES2017' main.ts
Solid tsconfig
Based on those results, it looks like this is an okay tsconfig
.
{ "compilerOptions": { "target": "es5", "module": "<not None>", "experimentalDecorators": true, "strict": false }}
Final Notes
- I didn't test as many compiler options as I could have. If there's interest, I can update the repo later.
- I still don't have a good answer for OP's issue and that bothers me.
- I did check TS version as well. OP was using
2.4.2
and I'm using2.7.2
. Just to make sure that wasn't the issue, I bumped my version down too.
'this' is undefined because you are not properly configuring the property descriptor.
Instead of:
Object.defineProperty(target, key, { get: getter, set: setter, enumerable: true, configurable: true });
Do something like:
Object.defineProperty(target, key, { get() { // this is defined }, set(value: any) { // this is defined }, enumerable: true, configurable: true });
I had the same issue and the solution above solves it.
you can try this:
instead of
function logProperty(target: any, key: string) { ...}
using:
const logProperty = (target: any, key: string) => { ...}
because =>
's this
just outside, so it can get it.hope it's helpful!