【问题标题】:'this' is Undefined in TypeScript Property Decorator'this' 在 TypeScript 属性装饰器中未定义
【发布时间】:2018-01-31 12:31:26
【问题描述】:

我正在尝试理解 TypeScript 装饰器(特别是针对属性),并根据我看到的一些示例提出了以下代码:

decorator.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');

我的问题是,当我尝试运行它时,我得到:

TypeError: 无法读取未定义的属性“firstName”

this 的值似乎是未定义的。我错过了什么?

作为参考,我的tsconfig.json 有:

"target": "es5"
"experimentalDecorators": true
"strict": false

8/27 更新 似乎只有当装饰器位于不同的 .ts 文件中时才会出现此问题。如果将装饰器放在不同的文件中并从另一个文件中导入,则会发生错误。但是,将它们全部放在同一个文件中不会导致问题。我只是误解了this 的解释方式吗?

【问题讨论】:

  • 你在哪一行得到错误?
  • 第一次引用this,所以往下看第三行。
  • 奇怪,在我这边和playground中都可以正常工作
  • 谢谢@AlekseyL .. 它似乎也在操场上为我工作。问题是我在项目设置中将我的代码作为节点模块运行,所以两者之间有些不同,只是无法弄清楚是什么......您可以尝试将代码粘贴到文件中(或从this repo 创建一个新文件)以重现错误。
  • 我遇到了同样的问题,但是我的 logProperty 装饰器是在同一个文件中定义的。但是,this 未定义。任何帮助将不胜感激。

标签: javascript typescript properties this decorator


【解决方案1】:

tl;dr: 我不确定为什么 OP 的配置不起作用;它现在似乎工作得很好。请参阅下面的一些蛮力测试。

猜猜

我想知道您是否以某种方式接错了tsconfig。我看过your repo's tsconfig,它看起来是正确的。是否有可能另一个配置文件感染了这些运行?我看到there were no automated tests there

测试

我今天遇到了类似的问题,并使用 OP 作为蓝图将a quick test 放在一起。我提取了编译器选项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}`);
        }
    }
}

结果

你可以看到完整的构建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.ts
Testing 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.ts
Testing 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.ts
Testing 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.ts
Testing 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.ts
Testing 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.ts
Testing 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

实心tsconfig

根据这些结果,tsconfig 看起来不错。

{
  "compilerOptions": {
    "target": "es5",
    "module": "<not None>",
    "experimentalDecorators": true,
    "strict": false
  }
}

最后说明

  • 我没有尽可能多地测试编译器选项。如果有兴趣,我可以稍后更新 repo。
  • 对于 OP 的问题,我仍然没有很好的答案,这让我很困扰。
  • 我也检查了 TS 版本。 OP was using 2.4.2I'm using 2.7.2。只是为了确保这不是问题,I bumped my version down too

【讨论】:

【解决方案2】:

'this' 未定义,因为您没有正确配置属性描述符。

代替:

Object.defineProperty(target, key, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true
    });

执行以下操作:

Object.defineProperty(target, key, {
      get() {
         // this is defined
      },
      set(value: any) {
         // this is defined
      },
      enumerable: true,
      configurable: true
    });

我遇到了同样的问题,上面的解决方案解决了。

【讨论】:

    【解决方案3】:

    你可以试试这个:

    而不是

    function logProperty(target: any, key: string) {
    
     ...
    }
    

    使用:

    const logProperty = (target: any, key: string) => {
     ...
    }
    

    因为=&gt;this 就在外面,所以它可以得到它。 希望对您有所帮助!

    【讨论】:

      【解决方案4】:

      我使用 allen wang post 构建并提出了这个

      export const 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 (val) {
              Object.defineProperty(target, key, {
                  get: getter,
                  set: setter,
                  enumerable: true,
                  configurable: true
              });
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2018-07-20
        • 2015-10-23
        • 2019-07-29
        • 1970-01-01
        • 1970-01-01
        • 2021-06-25
        • 2020-06-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多