TypeScript@5のDecoratorを動かしてみる
publication: 2023/01/31
update:2024/02/20
TypeScript5 と TC39/Stage3 への対応状態
検証バージョン
typescript@5.0.0-betaでの検証です。
検証に使用したテストプログラムは以下の場所に置いてあります。
https://github.com/SoraKumo001/typescript-decorator
使える Decorator 一覧
ClassDecorator
ClassMethodDecorator
ClassGetterDecorator
ClassSetterDecorator
ClassFieldDecorator
ClassAccessorDecorator
Parameter 関連の Decorator はありません。
DecoratorContext の access プロパティは pending になっています。
動作確認
テストプログラム
typescript@5.0.0-betaで動作するようにテストプログラムを書きました。
Decorator には標準対応しているので、tsconfig.jsonは標準のままで動作します。
1type AnyFunction = (...args: any[]) => any;23type S3ClassDecorator = (4 value: Function,5 context: ClassDecoratorContext6) => void | (new () => any);78type ClassMethodDecorator = (9 value: Function,10 context:11 | ClassMethodDecoratorContext12 | ClassGetterDecoratorContext13 | ClassSetterDecoratorContext14) => void | AnyFunction;1516type ClassFieldDecorator = (17 value: undefined,18 context: ClassFieldDecoratorContext19) => (initialValue: unknown) => any | void;2021type ClassAccessorDecorator = (22 value: ClassAccessorDecoratorTarget<unknown, unknown>,23 context: ClassAccessorDecoratorContext24) => ClassAccessorDecoratorResult<unknown, any>;2526const classDecorator: S3ClassDecorator = (value, context) => {27 context.addInitializer(() =>28 console.log(`初期化:${context.kind}:${String(context.name)}`)29 );30 console.log(value, context);31};3233const classFieldDecorator: ClassFieldDecorator = (value, context) => {34 console.log(value, context);35 return (initialValue) =>36 `取得:${context.kind}:${String(context.name)} => ${initialValue}`;37};3839const classMethodDecorator: ClassMethodDecorator = (value, context) => {40 console.log(value, context);41 context.addInitializer(() =>42 console.log(`初期化:${context.kind}:${String(context.name)}`)43 );44 return function (this: unknown, ...args: any[]) {45 return `取得:${context.kind}:${String(context.name)} => ${value.apply(46 this,47 args48 )}`;49 };50};5152const classAccessorDecorator: ClassAccessorDecorator = (value, context) => {53 console.log(value, context);54 return {55 get(this) {56 return `取得:${context.kind}:${String(context.name)} => ${value.get.apply(57 this58 )}`;59 },60 };61};6263console.log("START");6465@classDecorator66class Test1 {67 @classAccessorDecorator68 a = 1;69 @classFieldDecorator70 b = 10;71 @classMethodDecorator72 get c() {73 return 123;74 }75 @classMethodDecorator76 add(a: number, b: number) {77 return a + b;78 }79}8081console.log("test1");82const test = new Test1();83console.log(test.a);84console.log(test.b);85console.log(test.c);86console.log(test.add(10, 20));8788console.log("test2");89const test2 = new Test1();90console.log(test2.a);91console.log(test2.b);92console.log(test2.c);93console.log(test2.add(10, 20));9495console.log("END");
出力結果
1START2{ get: [Function: get a], set: [Function: set a] } {3 kind: 'accessor',4 name: 'a',5 static: false,6 private: false,7 addInitializer: [Function (anonymous)]8}9[Function: get c] {10 kind: 'getter',11 name: 'c',12 static: false,13 private: false,14 addInitializer: [Function (anonymous)]15}16[Function: add] {17 kind: 'method',18 name: 'add',19 static: false,20 private: false,21 addInitializer: [Function (anonymous)]22}23undefined {24 kind: 'field',25 name: 'b',26 static: false,27 private: false,28 addInitializer: [Function (anonymous)]29}30[class Test1] {31 kind: 'class',32 name: 'Test1',33 addInitializer: [Function (anonymous)]34}35初期化:class:Test136test137初期化:getter:c38初期化:method:add39取得:accessor:a => 140取得:field:b => 1041取得:getter:c => 12342取得:method:add => 3043test244初期化:getter:c45初期化:method:add46取得:accessor:a => 147取得:field:b => 1048取得:getter:c => 12349取得:method:add => 3050END
まとめ
legacy の Decorator と比べると完全に構造が変わっており、古いプログラムをこちらに対応させようとすると、それなりに書き換えが必要です。また ParameterDecorator が現時点で存在していないので、それに依存したプログラムだと移植は難しいでしょう。なかなか使いどころが難しい感じになってしまいました。