🌟
TypeScript@5のDecoratorを動かしてみる
TypeScript5 と TC39/Stage3 への対応状態
検証バージョン
typescript@5 が beta に到達したので、typescript@5.0.0-beta
での検証を行いました。
検証に使用したテストプログラムは以下の場所に置いています。
使える Decorator 一覧
- ClassDecorator
- ClassMethodDecorator
- ClassGetterDecorator
- ClassSetterDecorator
- ClassFieldDecorator
- ClassAccessorDecorator
Parameter 関連の Decorator はありません。
また、DecoratorContext の access プロパティは pending になっています。
動作確認
テストプログラム
typescript@5.0.0-beta
で動作するようにテストプログラムを書きました。
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 が現時点で存在していないので、それに依存したプログラムだと移植は難しいでしょう。なかなか使いどころが難しい感じになってしまいました。
Discussion