空雲 Blog

Eye catchTypeScript@5のDecoratorを動かしてみる

publication: 2023/01/31
update:2023/06/22

TypeScript5 と TC39/Stage3 への対応状態

検証バージョン

[email protected]での検証です。
検証に使用したテストプログラムは以下の場所に置いてあります。
https://github.com/SoraKumo001/typescript-decorator

使える Decorator 一覧

  • ClassDecorator

  • ClassMethodDecorator

  • ClassGetterDecorator

  • ClassSetterDecorator

  • ClassFieldDecorator

  • ClassAccessorDecorator

Parameter 関連の Decorator はありません。
DecoratorContext の access プロパティは pending になっています。

動作確認

テストプログラム

[email protected]で動作するようにテストプログラムを書きました。
Decorator には標準対応しているので、tsconfig.jsonは標準のままで動作します。

type AnyFunction = (...args: any[]) => any; type S3ClassDecorator = ( value: Function, context: ClassDecoratorContext ) => void | (new () => any); type ClassMethodDecorator = ( value: Function, context: | ClassMethodDecoratorContext | ClassGetterDecoratorContext | ClassSetterDecoratorContext ) => void | AnyFunction; type ClassFieldDecorator = ( value: undefined, context: ClassFieldDecoratorContext ) => (initialValue: unknown) => any | void; type ClassAccessorDecorator = ( value: ClassAccessorDecoratorTarget<unknown, unknown>, context: ClassAccessorDecoratorContext ) => ClassAccessorDecoratorResult<unknown, any>; const classDecorator: S3ClassDecorator = (value, context) => { context.addInitializer(() => console.log(`初期化:${context.kind}:${String(context.name)}`) ); console.log(value, context); }; const classFieldDecorator: ClassFieldDecorator = (value, context) => { console.log(value, context); return (initialValue) => `取得:${context.kind}:${String(context.name)} => ${initialValue}`; }; const classMethodDecorator: ClassMethodDecorator = (value, context) => { console.log(value, context); context.addInitializer(() => console.log(`初期化:${context.kind}:${String(context.name)}`) ); return function (this: unknown, ...args: any[]) { return `取得:${context.kind}:${String(context.name)} => ${value.apply( this, args )}`; }; }; const classAccessorDecorator: ClassAccessorDecorator = (value, context) => { console.log(value, context); return { get(this) { return `取得:${context.kind}:${String(context.name)} => ${value.get.apply( this )}`; }, }; }; console.log("START"); @classDecorator class Test1 { @classAccessorDecorator a = 1; @classFieldDecorator b = 10; @classMethodDecorator get c() { return 123; } @classMethodDecorator add(a: number, b: number) { return a + b; } } console.log("test1"); const test = new Test1(); console.log(test.a); console.log(test.b); console.log(test.c); console.log(test.add(10, 20)); console.log("test2"); const test2 = new Test1(); console.log(test2.a); console.log(test2.b); console.log(test2.c); console.log(test2.add(10, 20)); console.log("END");

出力結果

START { get: [Function: get a], set: [Function: set a] } { kind: 'accessor', name: 'a', static: false, private: false, addInitializer: [Function (anonymous)] } [Function: get c] { kind: 'getter', name: 'c', static: false, private: false, addInitializer: [Function (anonymous)] } [Function: add] { kind: 'method', name: 'add', static: false, private: false, addInitializer: [Function (anonymous)] } undefined { kind: 'field', name: 'b', static: false, private: false, addInitializer: [Function (anonymous)] } [class Test1] { kind: 'class', name: 'Test1', addInitializer: [Function (anonymous)] } 初期化:class:Test1 test1 初期化:getter:c 初期化:method:add 取得:accessor:a => 1 取得:field:b => 10 取得:getter:c => 123 取得:method:add => 30 test2 初期化:getter:c 初期化:method:add 取得:accessor:a => 1 取得:field:b => 10 取得:getter:c => 123 取得:method:add => 30 END

まとめ

legacy の Decorator と比べると完全に構造が変わっており、古いプログラムをこちらに対応させようとすると、それなりに書き換えが必要です。また ParameterDecorator が現時点で存在していないので、それに依存したプログラムだと移植は難しいでしょう。なかなか使いどころが難しい感じになってしまいました。