水無瀬のプログラミング日記

プログラムの勉強した時のメモ

TypeScriptにeslint + prettierを導入する

はじめに

毎回新しいプロジェクトを作るたびにeslintとprettierの設定方法を忘れるので、
良い加減忘れないようにまとめておく。

インストール

# eslintとeslintのplugin
$ npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

# prettierとplugin
$ npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier

設定ファイルの追加

.eslintrc.js

TypeScriptの対応に必要な箇所だけまとめる。
ルールはtslintとの対応表eslint公式のルールを見ながら好みのものを入れればOK。

module.exports = {
  'parser': '@typescript-eslint/parser',
  'env': {'browser': true, 'node': true, 'es6': true},
  'parserOptions': {
    'sourceType': 'module'
  },
  'plugins': ['@typescript-eslint'],
  'rules': {
    // 好みのルール
  }
};

.prettierrc

こちらも同じく好みのものを入れればOK。
設定項目は公式を参照。

{
  "singleQuote": true,
  "printWidth": 120
}

まとめ

eslintとprettierの設定をまとめた。
eslintの設定とかはプロジェクトによると思うので、
詳細については都度覚えていこうと思う。

参考リンク

ReactでStorybookを導入する

はじめに

前回、AngularでStorybookを導入したので、今回はReactでやってみる。
ついでに前から気になってたstyled-omponentsも試してみる。

TL;DR.

ソースコード

Reactプロジェクト作成

create-react-appを使ってサクッと作る。

$ npx create-react-app react-component --typescript

Storybook導入

Reactについても公式に手順があるので参考にしながら進める。

# create-react-appを使っている場合こっち
$ npx -p @storybook/cli sb init --type react_scripts

# 使わなかった場合は
$ npx -p @storybook/cli sb init --type react

TypeScriptに対応させる

上記手順で導入した場合、js向けに作成されるのでtsに対応させる。

main.jsを修正する

初期状態だとstories: ['../src/**/*.stories.js']となっているので、
stories: ['../src/**/*.stories.ts']と修正しておく。

module.exports = {
  stories: ['../src/**/*.stories.ts'],
  addons: [
    '@storybook/preset-create-react-app',
    '@storybook/addon-actions',
    '@storybook/addon-links',
  ],
};

tsconfig.jsonの追加

ざっくり対象になるよう./storybook配下に追加。

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "types": [
      "node"
    ]
  },
  "exclude": [
    "../src/test.ts",
    "../src/**/*.test.tsx"
  ],
  "include": [
    "../src/**/*"
  ]
}

Storyを作成する

src/stories配下にButton.stories.ts(ファイル名は任意)を作成。
公式のサンプルとはちょっと違うが動いているので一旦これで良しとする。
※元のComponentは後述

import { ButtonComponent } from '../component/Button';

export default {
  title: 'Button',
  component: ButtonComponent
};

export const ButtonDefault = () => ButtonComponent({text: 'default'});
export const ButtonSmall = () => ButtonComponent({text: 'small', size: 'small'});
export const ButtonLarge = () => ButtonComponent({text: 'default', size: 'large'});
export const ButtonPrimary = () => ButtonComponent({text: 'primary', types: 'primary'});

作成したComponent

Button.tsx

内容はAngularでやったものと同じ。

import React from 'react';
import styled, { css } from 'styled-components';

type ButtonProps = {
  text: string,
  size?: 'small' | 'large',
  types?: 'primary' | 'error' | 'warning'
};

const Button = styled.button<Pick<ButtonProps, 'types' | 'size'>>`
  ${({size}) => getSize(size)}
  ${({types}) => getColor(types)}
`;

const getSize = (size: ButtonProps['size']) => {
  switch (size) {
    case 'small':
      return css`
        width: 72px;
        height: 30px;
        font-size: 10px;
      `;
    case 'large':
      return css`
        width: 300px;
        height: 100px;
        font-size: 24px;
      `;
    default:
      return css`
        width: 150px;
        height: 50px;
        font-size: 16px;
      `;
  }
}
const getColor = (type?: ButtonProps['types']) => {
  switch(type) {
    case 'primary':
      return css`
        background-color: #007bff;
        border: 1px solid #007bff;
        border-radius: 5px;
        color: #fff;
      `;
    default:
      return css`
        background-color: #fff;
        border: 1px solid #000;
        color: #333;
      `;
  }
}

export const ButtonComponent = (props: ButtonProps) => <Button size={props.size} types={props.types}>{props.text}</Button>;

実行してみる

今回も追加されたscriptがあるので、yarn storybookを実行すれば良い。

f:id:minase_mira:20200309212707p:plain 画像の様にブラウザが立ち上がり、表示されればOK。

おまけ

styled-componentsの導入は下記コマンドでOK。

$ yarn install --dev styled-components

まとめ

今回はReactでStorybookを導入した。
Storybookよりもstyled-components使ってComponent作るほうが大変だった。。。
Angular,ReactとやったのでそのうちVueもやってみようかな。

参考リンク

AngularでStorybookを導入する

はじめに

storybookが良いと聞く今日このごろ。
Angularでも使えることを知ったので、今回はそれを試してみる。

TL;DR.

ソースコード

Angularプロジェクト作成

CLIでサクッと作成する。

$ ng new ng-component

Storybook導入

公式サイトに手順があるので、それを参考に進める。
これだけでセットアップが完了する。

$ npx -p @storybook/cli sb init --type angular

サンプルプログラムを追加してくれるが、
パッケージがなくうまく動かなかったりしたのでstoryを作っていく。

Storyを作成する

src/stories配下にbutton.component.stories.ts(任意)を作成する。
ButtonComponentの中身は後述

import { ButtonComponent } from '../app/component/button/button.component';

export default {
  title: 'button component',
};

export const ButtonDefault = () => ({
  component: ButtonComponent,
  props: {
    text: 'default'
  }
});

作成したcomponent

button.component.ts

import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: 'app-button',
  templateUrl: './button.component.html',
  styleUrls: ['./button.component.scss']
})
export class ButtonComponent implements OnInit {
  @Input() text: string;

  buttonClasses: string[] = ['button'];

  ngOnInit() {
  }

}

button.component.scss

.button {
  width: 150px;
  height: 50px;
  background-color: #fff;
  border: 1px solid #000;
  color: #333;
  font-size: 16px; 
}

button.component.html

<button [ngClass]="buttonClasses">{{text}}</button>

実行してみる

自動で追加されたnpm scriptがあるので、それを実行すればOK。

$ npm run storybook
> ブラウザが勝手に立ち上がり表示される

f:id:minase_mira:20200308223047p:plain 画像の様に出てくれば完了。

まとめ

今回は前々からやろうと思ってたstorybookの導入をやった。
component単位で見れるものがあるのは、やっぱり楽だと思うので今後は作っていきたいところ。
github pagesで見れると楽なのでいつか対応したい。

参考リンク

Firebase Functionsを定期実行する

はじめに

Firebase Functionsで定期実行をできることを知ったので今回はそれを試してみる。

TL;DR.

試したソースコード
(既存のプロジェクトでやったため、最小構成ではない)

Firebase(+ GCP)の設定

プロジェクト作成

f:id:minase_mira:20200303233157p:plainf:id:minase_mira:20200303233202p:plainf:id:minase_mira:20200303233208p:plain
Firebaseプロジェクト作成
WebUIからポチポチ設定していく。
名前決めるくらいなのでサクッとすすめる。
アナリティクスはいらなかったので無効にする。

f:id:minase_mira:20200303233211p:plain プロジェクト作成できたら左上の歯車からSetting画面を開く。
定期実行する場合Google Cloud Platform(GCP)リソース ロケーションを設定する必要があるので、
好きなロケーションを選択する。(今回はasia-northeast1(東京)にしておく)

作成したプロジェクトが無料プラン(Spark)だった場合、従量課金制のBlazeにしておく。
(定期実行するのに必要)

GCPの設定

Cloud Scheduler APICloud Pub/Sub APIを有効にする必要がある。
APIライブラリから有効にしておく。

Firebase CLIの設定

# firebaseにログイン
$ firebase login

# firebase functions 初期設定
$ firebase init functions
> 色々聞かれるので好みの設定をする
> .firebasercとfirebase.json、functionsディレクトリが作成されていればOK

デプロイするフォルダを変更する

デフォルトの設定では作成されたfunctionsディレクトリにあるpackage.jsonを参照してデプロイされる。
このpackage.jsonで依存とか解決しているらしい。
※初期設定だとfunctions/lib/index.jsがデプロイ先になる。

ただ、今回はすでにあるプロジェクトをデプロイしたかったので、そのへんを変更していく。
(+階層が1段ずれるのもデプロイのためだけに変更するのも嫌だったので)

変更するにはfirebase.json"source": "."を追加すれば良い。
(今回はカレントディレクトリにしたかったため.)
合わせてpredeployfunctions配下のpackage.jsonを見るようprefixが付いていたので消しておく。

{
  "functions": {
    "source": ".",
    "predeploy": "npm run build"
  }
}

定期実行するコードを書いていく

公式のサンプルを参考にしながら書いていく。

export const sampleJob = functions
  // regionはGCPリソース ロケーションと合わせる
  .region('asia-northeast1')
  // 時間設定。よくあるcronの書き方もできる
  .pubsub.schedule('every day 07:00')
  // 時間指定しているので、一応タイムゾーンを設定しておく
  .timeZone('Asia/Tokyo')
  .onRun(context => {
    // 処理
  }

webpackの設定

webpackでバンドルした結果をデプロイしたかったので設定していく。
多分firebase用の設定はあまりないはず。

const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  mode: 'production',
  entry: path.join(__dirname, '/app.ts'),
  target: 'node',
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: [
          {loader: 'ts-loader'},
          {loader: 'eslint-loader'}
        ],
        exclude: /node_modules/
      },
      {
        test: /\.node$/,
        use: 'node-loader'
      }
    ]
  },
  resolve: {
    extensions: ['.js', '.json', '.ts'],
    modules: [path.join(__dirname, 'src'), 'node_modules']
  },
  output: {
    // これを設定しないとうまくデプロイされなかった
    libraryTarget: 'this',
    path: path.join(__dirname, 'dist'),
    filename: 'app.js'
  },
  externals: [nodeExternals()]
};

環境変数を設定する

パスワードなどコード上に書きたくないものを環境変数に設定していく。
公式のサンプルを真似てすすめる。

# 環境変数にslackのwebhookURLを設定
$ firebase functions:config:set slack.url=https://hooks.slack.com/services/XXX

取り出すには下記のようにする。

import { config } from 'firebase-functions';
const slackUrl = config().slack.url;

デプロイする

firebase deploy —only functionsでOK。
ただし、deploy complete!とか言いながら失敗することがあるので、
Webコンソールを確認してデプロイが完了してることを確認した方が良いかも。

まとめ

Firebase Functionsで定期実行する方法をまとめた。
今まで定期実行したい処理がある時はLambdaを使ってたけど、今後はfirebaseで良さそう。
(個人的にGCPに集約したいだけ。)

参考リンク

IntelliJでファイル保存時にactive fileにのみPrettierをかける

はじめに

一身上の都合により、ファイル保存時にアクティブファイルにのみPrettierをかけることになった。
調べればすぐ出てくるかなと思ったけど、意外と出てこなかったのでメモを残す。

TL;DR.

f:id:minase_mira:20200204221215p:plain

準備

File WatchersPrettierプラグインをインストールしておく。

File Watchersの設定

Prettierを叩く設定を行う。
Preference > Tools > File Watchersを開く。 f:id:minase_mira:20200204221444p:plain

左下のを押すとTemplateを選べるので、Prettierを選択する。
f:id:minase_mira:20200204221517p:plain

出てきたポップアップのFiles to Watch > File Typeで好みのファイルを選ぶ。
(今回はTypeScriptを指定。)
Tool to Run on Changes > Arugments—writeの後ろを$FilePathRelativeToProjectRoot$に変更する。
(INSERT MACRO...を選択し、FilePathRelativeToProjectRootを選択でもOK) f:id:minase_mira:20200204221215p:plain

これで設定は完了。

まとめ

保存するたびに全部変えても問題ないと思うけど、
逐一全部変わるのもっていうのと触ってないファイルまで変わるのが嫌だったので、
今回はアクティブファイルに絞った。
(VSCodeのPrettierと同じ挙動だと思う)

調べてもぱっと出てこなくてちょっとハマったけど、
こういうことやる人そこまで多くないんだろうか。。。

それでは、今回はこの辺で。

Github Actionsを使ってfirebaseへデプロイする

はじめに

最近Github Actionsをよく聞くので使ってみたい今日このごろ。
ちょうど最近自分のポートフォリオ?のようなマイページのサイトを作っていたので、
これをfirebaseにデプロイするようにGithub Actionsを設定してみる。

TL;DR.

ymlファイル

Github Actions準備

firebaseに上げる前にGithub Actionsの準備を行う。
始めるにはWebからやるか、自分で作りたければ.github/workflow/xxx.ymlを用意する。

Webからやる場合

Code,Pull requestsが並ぶところにActionsがあるのでそこを開く。
テンプレートがたくさんあるので、ここから選べばよしなにymlファイルを作成してくれる。
今回は自分で作って見たかったので、こちらの手順は割愛。

f:id:minase_mira:20200201001250p:plain f:id:minase_mira:20200201001313p:plain

自分で作る場合

自力でテンプレにあるようなymlファイルを書けば良い。
構文は公式のドキュメントがあるので、それを見ながら書いていく。

# ワークフローの名前
# 書かないとデフォルト値(リポジトリのルートに対するワークフローファイルの相対パス)になる
name: inform-weather-confirm-flow

# ワークフローをトリガーするGithubイベントの名前
on:
  # 1個の場合そのまま書く
  # 2個以上の場合、リスト形式で書く→[push, pull_request]
  pull_request:
    # ブランチを指定すれば絞ることができる
    # ワイルドカード、!が使える
    branches:
      # マスター以外
      - '!master'

# ワークフローで実行するジョブを書いていく
jobs:
  # ジョブ名
  deploy:
    # ジョブを実行する環境
    # 環境については公式ドキュメントを参照
    runs-on: ubuntu-latest
    # ジョブに紐づくタスク
    steps:
      # アクションを実行する
      # アクション = 再利用可能なコードの単位
      - uses: actions/checkout@v1

      # nameはstepの名前
      - name: setup 
        uses: actions/setup-node@v1
        with:
          node-version: 12

      # パッケージインストール
      - name: install
        run: npm install

      # lint実行
      - name: lint
        run: npm run lint:format

      # テスト実行
      - name: test
        run: npm test

firebaseへのデプロイ

プロジェクト作るところは割愛。
GUIでポチポチすればよかったはず。

# ciで使うトークンを取得
$ firebase login:ci
> browserでログインを求められるのでログイン
> Tokenが出てくるのでそれをコピーしておく

# コマンドは下記の通り
$ firebase deploy --token=とったトークン

これをymlに書けば良い。
今回はnpm scriptに下記の通り追加してこのコマンドを使う。

"script": {
  "deploy": "firebase deploy"
}
# deployするsample workflow
name: deploy-sample

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1

      - name: setup
        uses: actions/setup-node@v1
        with:
          node-version: 12

      - name: install
        run: npm install

      - name: build
        run: npm run build

      - name: deploy
        # tokenはsecretsに追加し、それを参照する
        run: npm run deploy -- --token=${{secrets.token}}

おまけ

特定のジョブが終わっていることを条件にしたい場合

testが通った後にデプロイをしたい場合などで割とよくあるパターンだと思う。
実行したいジョブにneedsで事前に完了していてほしいジョブを指定する。

例は下記の通り。

jobs:
  job1:
  job2:
    # job1が完了していること
    needs: job1
  job3:
    # job1とjob2が完了していること
    needs: [job1, job2]

パスワード/Tokenなどを設定したい場合

デプロイする時にパスワードなりtokenなり他の人に見せたくないけど、
ci上で使いたい!っていうものが多々ある。
circleciとか他のci/cdサービスでこのようなことは普通にできると思うが、
Github Actionsでももちろん設定できる。
登録するにはWebUIからポチポチしていけばできる。
Settings > Secrets > Add a new secretから追加できる。

f:id:minase_mira:20200201001345p:plain

使う時は下記のようにすればOK。

- name: deploy
  # ${{secrets.XXX}}で取得できる
  run: npm run deploy -- --token=${{secrets.TOKEN}}

Slackへの通知

デプロイ結果やビルドエラーになった場合など、
どこかに通知したいこともあると思う。
今回は個人的によく使うので、Slackに通知することにする。

既にSlackに通知をするActionがあるので、これを使うことにする。
webhookのトークンが必要なので予め取得しておく。
設定項目は公式にも書いてあるが下記の通り。

...
- name: slack-notice-success
        uses: rtCamp/action-slack-notify@v2.0.0
        env:
          SLACK_CHANNEL: 'チャンネル名'
          SLACK_COLOR: '#3278BD'
          SLACK_ICON: 'icon url'
          SLACK_MESSAGE: 'メッセージ'
          SLACK_TITLE: 'タイトル'
          SLACK_USERNAME: '名前'
          SLACK_WEBHOOK: ${{ secrets.SLACK_TOKEN }}
...

画像みたいな通知が飛んでくればOK。 f:id:minase_mira:20200201001217p:plain

まとめ

今回はGithub Actionsでfirebaseにデプロイするワークフローを作成した。
前まではCircleCIを使っていたけど、
Githubで完結するのがだいぶ楽なのでとても良かった。
いずれもっと複雑なものを書いていこうと思う。
それでは今回はこの辺で。

参考サイト

TypeScriptのDecoratorまとめ

はじめに

Decorator使ってみることになったが何もわからないのでまとめる。

TL;DR.

コード

準備

デフォルトだと使えないので、下記の通りtsconfigを修正する必要がある。
おそらくtsc —initの結果に"experimentalDecorators": trueの追加で問題ないはず。
(cliの場合同様のオプションを指定すればOK)

{
    "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true
    }
}

使い方

@hogeの形式でclass, method, accessor, property, parameterにつけられる。
ただし、hogeはdecoratorを付けた場所の情報と共に実行時呼び出される関数である必要がある。
+型定義とかdeclare classにはつけられない。
何言っているか自分でもわからないので、使い方はサンプルを添えてまとめる。

Class Decorators

Class Decoratorはクラスのコンストラクターに適用され、
クラス定義の監視、変更、置換のために使用できる。
使うときはclassの前に付ければ良い。

// classDecoratorは引数に付けたクラスのconstrouctorを受け取る
function classDecorator(constructor: Function) {

}

@classDecorator
class SampleClass {
}

使い方は下記の様にする。
下記の例ではconstructorとそのprototypeObject.sealをかけている。

function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

@sealed
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

Class Decoratorが値を返却する時は、その値でconstructorの内容を上書きできる。

function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
  return class extends constructor {
      newProperty = "new property";
      hello = "override";
  }
}

@classDecorator
class Greeter {
  property = "property";
  hello: string;
  constructor(m: string) {
      this.hello = m;
  }
}

console.log(new Greeter("world"));

出力結果は下記の通り。
constructorの結果が上書きされていることがわかる。

class_1 {
  property: 'property',
  hello: 'override',
  newProperty: 'new property'
}

Method Decorators

method decoratorはメソッドに適用でき、
メソッド定義の観察、変更、または置換に使用できる。
使う時はmethodの前に付ければ良い。

    /**
     * 下記のpropertyを受け取る
     * @param target decoratorを付けたmethodのclassのprototype
     * @param propertyKey decoratorを付けたmethodの名前
     * @param descriptor methodのproperty descriptor ※targetがes5未満だとundefinedになる
     */
    function methodDecoratorSample(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    
    }
    
    class SampleClass {
      @methodDecoratorSample
      greet(message: string) {
        return `Hello! ${message}`;
      }
    }

使い方は下記の通り。
descriptor.valueの結果を書き換えることで、メソッドの戻り値を変更できる。
argumentsを使うことでdecorator付けたメソッドの引数を取れる。

function greetDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  descriptor.value = function () {
    return `こんにちは。${arguments[0]}`;
  };
}

class MethodDecoratorGreeter {
  @greetDecorator
  public greet(name: string) {
    return `Hi! ${name}`;
  }
}
console.log(new MethodDecoratorGreeter().greet('Tom')); // こんにちは。Tom

元のメソッドを実行したい場合は、Reflectを使うことでできる。
また、decoratorで引数を受け取りたい場合は、
必要な引数3つを受け取る関数を返す関数を作ることでできる。

function reflectDecorator(type: 'original' | 'change') {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // 後で実行したいので退避しておく
    const originalMethod = descriptor.value;
    switch(type) {
      case 'original':
        descriptor.value = function() {
          return Reflect.apply(originalMethod, this, arguments);
        }
        break;
      case 'change':
        descriptor.value = function() {
          return '変更した';
        }
    }
  }
}

class ReflectSample {
  @reflectDecorator('original')
  noCange() {
    return '元のメソッド';
  }

  @reflectDecorator('change')
  change() {
    return '元のメソッド';
  }
}
const reflectSample = new ReflectSample();
console.log(`noChange: ${reflectSample.noCange()}`); // 元のメソッド
console.log(`change  : ${reflectSample.change()}`); // 変更した

Accessor Decorators

method decoratorのaccessor版。
ほぼ同じなので割愛。

Property Decorators

今までのDecoratorとは違い、あまりできることが多くないかも?
使う時はpropertyの前に付ければ良い。

下記のようにPropertyDescriptorを指定することで、
propertyの内容を変更できる。
ただし、decoratorの戻り値はvoid or anyなので実質型は消え去る。
(ここだけなのでこだわる必要もあまりないと思うけど)

/**
 * 下記の引数を受け取る
  * @param target decoratorを付けたpropertyのclassのprototype
  * @param member プロパティ名
  */
function propertyDecoratorSample(target: any, member: string): any {
  const propertyDescriptor: PropertyDescriptor = {
    configurable: false,
    enumerable: false,
    value: 'huga',
    writable: true
  }
  return propertyDescriptor;
}


class SampleClassProperty {
  @propertyDecoratorSample
  private name?: string;

  constructor(name: string) {    
    // 上書きする前は、decoratorの戻り値で指定した内容になっている
    this.name = name;
  }
}

console.log(new SampleClassProperty('hoge'));// SampleClassProperty { name: 'hoge' }

Parameter Decorators

ParameterDecoratorsは、メソッドでパラメータが宣言されたことを確認するためにのみ使用できる。
ParameterDecoratorsの戻り値は無視される。
使う時はparameterの前に付ければ良い。

import 'reflect-metadata';

const metaDataKey = Symbol('sample');

/**
 * 下記のpropertyを受け取る
  * @param target decoratorを付けたclassのprototype
  * @param member memberの名前
  * @param parameterIndex 関数のパラメーターリスト内のパラメーターのインデックス
  */
function parameterDecoratorSample(target: any, member: string, parameterIndex: number) {
  const parameters = Reflect.getOwnMetadata(metaDataKey, target, member) || [];
  parameters.push(parameterIndex);
  // metadata付与
  Reflect.defineMetadata(metaDataKey, parameters, target, member);
  
}

function methodDecorator(target: any, propKey: string, desc: PropertyDescriptor) {
  const method = desc.value;
  desc.value = function() {
    // 付与したメタデータ取得
    const parameters = Reflect.getMetadata(metaDataKey, target, propKey);
    
    // パラメータチェック
    if (parameters) {
      for (const parameterIndex of parameters) {
        if (parameterIndex >= arguments.length || arguments[parameterIndex] === undefined ) {
          throw new Error('Missing required argument!');
        }
      }
    }
    // 問題なければ元のメソッドを実行する
    return method.apply(this, arguments);
  }
  
}

class SampleClassParameter {
  @methodDecorator
  greet(@parameterDecoratorSample name: string) {
    return `Hello! ${name}`;
  }
}

console.log(new SampleClassParameter().greet('Tom'));

まとめ

TypeScriptのDecoratorsについてまとめた。
思ったより便利ではあったが、
一部Reflectを使わないと行けないところもあり、 複雑なところもある印象。
ただ、便利なので使える場所では使っていきたいところ。

ちなみに、DecoratorはECMAScriptでもstage2なので、いずれJSにも来るかもしれない。

それでは今回はこの辺で。

参考リンク