NestJSで@Controller
のような@
を使った書き方をよくみる。よくわからず使っていたけど調べてみたらなんてことなかったので書き留めておく。
こういうやつ。
@Controller('app') export class AppController { @Post() create(@Body() dto: CreateAppDto) {} }
Decoratorっていうjsの機能らしい
DecoratorはJavaとかPythonでは普通に使われている概念らしく、jsではES2016から導入されたみたい。詳しくは下記。
Exploring EcmaScript Decorators. Iterators, generators and array… | by Addy Osmani | Google Developers | Medium
何に使えるのかって言うとメソッド実行時にログを出力したり、引数のチェックをしたり、依存関係の注入(DI)に使える。NestJSではDIとして使われていることが多い。@Injectable
みたいなやつね。
サクッと試すための環境を整える
実際にコードを書いて試す前に環境を整える。vimのquickrunを使う前提。quickrun使わなくても普通にts-node
コマンドで実行してもいいので、vimmerじゃない人は飛ばしちゃってOK。
ts-node test.ts
~/tsconfig.json
~/tsconfig.json
{ "compilerOptions": { "target": "ES2016", "module": "commonjs", "lib": ["ES2016", "dom"], "alwaysStrict": true, "experimentalDecorators": true } }
experimentalDecorators:true
が肝。これがないとDecoratorが使えない。alwaysStrict: true
で実行時にエラーが出るようにする。
~/test.ts
class Cat { say() { console.log('nya-!') } } const cat = new Cat() cat.say() // nya-!
このファイルで色々実験していく
quickrunの設定
let g:quickrun_config={ \'_': { \ 'split': '' \}, \} set splitbelow " \rで保存して実行、画面分割を下に出す nnoremap \r :cclose<CR>:write<CR>:QuickRun -mode n<CR> xnoremap \r :<C-U>cclose<CR>:write<CR>gv:QuickRun -mode v<CR>
\r
でquickrunを実行。
早速Decoratorを試してみる
まずは@XXXXX
として使えるようなDecorator関数を作る。今回は@readonly
として使えるような関数を作る。
~/test.ts
function readonly(target: any, key: string, descriptor:PropertyDescriptor) { descriptor.writable = false return descriptor }
注意したい点は引数にtarget
, key
, descriptor
をとるということ。これだけ抑えていれば問題ない。
実際に使うときは下記のような感じ。
function readonly(target: any, key: string, descriptor:PropertyDescriptor) { descriptor.writable = false return descriptor } class Cat { @readonly say() { console.log('nya-!') } } const cat = new Cat() cat.say()
この状態では何もエラーは出ないが、say()
を書き換えようとすると、エラーを出してくれる。
function readonly(target: any, key: string, descriptor:PropertyDescriptor) { descriptor.writable = false return descriptor } class Cat { @readonly say() { console.log('nya-!') } } const cat = new Cat() cat.say() // 書き換え cat.say = function() { console.log('wan!') } // cat.say = function() { // ^ // TypeError: Cannot assign to read only property 'say' of object '#<Cat>'
めちゃシンプル。全然難しいことなかった。
引数を取るようなDecoratorを作ってみる
たとえばNestJSでよく見る@Get('/')
のようなDecoratorを作ってみる。
function Get(endpoint?: string) { return function(target: any, key: string, descriptor:PropertyDescriptor) { // ここでなにがしかをする console.log(endpoint) } } class Cat { @Get('/hoge') say() { console.log('nya-!') } }
あくまでイメージなので全く機能はないが、こんな感じで引数付きのDecoratorを作ることができる。
クラスにあてるDecoratorの作り方
先程作った@readonly
はメソッドにあてるDecorator。クラスにDecoratorをあてる場合、引数が異なってくる。
function Controller(fnc:Function) { console.log(fnc) // [class Cat] } @Controller class Cat { }
同様にプロパティやパラメータにあてるDecorator関数も存在する。詳細は下記。
終わり
NestJSのDecoratorの使い方はめちゃくちゃ勉強になる。他の言語書くときも使えるような考え方だから思想を知っておくのは絶対良い。