TypeScriptのデコレータで見かける5つのエラーパターン

デコレータとは

TypeScriptのデコレータ (Decorator) はクラス宣言とメンバーに注釈とメタプログラミング構文の両方を追加する方法を提供します。

JavaScriptにはない便利な機能ですが、使い方をよく理解していないとエラーを発生させてしまうことも少なくないです。

この記事では注意すべきTypeScriptのデコレータで見かける5つのエラーパターンについて記載しました。

1. 実行順は上から下ではない

例えば以下のようなデコレータ入りのClassがあるとする。

function first() {
  console.log('first(): factory evaluated')
  return function (
    _target: any,
    _propertyKey: string,
    _descriptor: PropertyDescriptor
  ) {
    console.log('first(): called')
  }
}

function second() {
  console.log('second(): factory evaluated')
  return function (
    _target: any,
    _propertyKey: string,
    _descriptor: PropertyDescriptor
  ) {
    console.log('second(): called')
  }
}

function third() {
  console.log('third(): factory evaluated')
  return function (
    _target: any,
    _propertyKey: string,
    _descriptor: PropertyDescriptor
  ) {
    console.log('third(): called')
  }
}

class ExampleClass {
  @first()
  @second()
  @third()
  method() {
    console.log('ExampleClass method(): called')
  }
}

const example = new ExampleClass()
example.method()

これを見て@first(), @second(), @third()の順にConsoleが実行されると勘違いされることがあるが、実際は…

first(): factory evaluated
second(): factory evaluated
third(): factory evaluated
third(): called
second(): called
first(): called
ExampleClass method(): called

この実行順を理解せずエラーを発生しているデコレータをたまに見かけます。

2. experimentalDecoratorsがtrueに設定されていない

デコレータを使用するにはtsconfig.jsonでexperimentalDecoratorsがtrueに設定されている必要があります。

tsc --initコマンドでtsconfig.jsonを生成した場合はexperimentalDecoratorsはコメントアウトされて無効になっています。

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

3. 先頭の@付け忘れ

デコレータの元になるのがfunctionを使用した関数なので、先頭に@を付け忘れるケースがある。

class ExampleClass {
  first()
  method() {
    console.log('ExampleClass method(): called')
  }
}

このような書き方はTypeScriptのコンパイル時に以下のようなエラーが表示される。

app.ts:14:3 - error TS2389: Function implementation name must be 'first'.

4. デコレータの挿入位置を間違える

前述の例はメソッド・デコレータなのでClass内のmethod()前に挿入していますが、ほかにもクラス・デコレータやプロパティ・デコレータなどがあり、用途によって挿入位置が違います。

もし用途とは違う場所にデコレータを記述した場合はエラーとなり、動作しません。

@first()
class ExampleClass {
  method() {
    console.log('ExampleClass method(): called')
  }
}

5. PropertyDecoratorと間違える

間違えてPropertyDescriptorではなくPropertyDecoratorを使用してしまうとエラーになる。

デコレータだし、コードヒントにも表示されるので慣れていないとPropertyDecoratorと間違えやすい。

function first() {
  console.log('first(): factory evaluated')
  return function (
    _target: any,
    _propertyKey: string,
    _descriptor: PropertyDecorator
  ) {
    console.log('first(): called')
  }
}

class ExampleClass {
  @first()
  method() {
    console.log('ExampleClass method(): called')
  }
}