0
0

はじめに

TypeScriptの本を読んでいると、デコレーターという物をみました。今までデコレーターってわかっているようでわかっていなかったので、ちょっと学んでみようかなぁと思って記事にしました。

Decoratorとは何か??

クラスやクラスのメンバー(メソッド、プロパティなど)に対して追加の機能を動的に付加するためのものらしいです。。うーん、、、ちょっとよくわからない。実際に具体例を交えながら学んでいこうと思います。

ソースコード

クラスデコレーター

クラス全体に対して適用されるデコレーターです。

classDecorator.ts
// biome-ignore lint/complexity/noBannedTypes: <explanation>
function Logger(target: Function) {
	console.log(`クラスが作成されました: ${target.name}`);
}

@Logger
class MyClass {
	constructor() {
		console.log("MyClassのインスタンスが生成されました");
	}
}

const myClassInstance = new MyClass();

実行結果
~/develop/typescript_decorator$ ts-node classDecorator.ts
クラスが作成されました: MyClass
MyClassのインスタンスが生成されました

メソッドデコレーター

メソッドに対して適用されるデコレーターです。

methodDecorator.ts
function Log(
	// biome-ignore lint/suspicious/noExplicitAny: This is needed to allow any type for the target
	target: any,
	propertyKey: string,
	descriptor: PropertyDescriptor,
) {
	console.log("111", target, propertyKey, descriptor);
	const originalMethod = descriptor.value;
	console.log("=======22 ", originalMethod);
	// biome-ignore lint/suspicious/noExplicitAny: This is needed to allow any type for the arguments
	descriptor.value = function (...args: any[]) {
		console.log(`メソッド ${propertyKey} が呼び出されました`);
		return originalMethod.apply(this, args);
	};

	return descriptor;
}

class MyClass {
	@Log
	myMethod() {
		console.log("メソッドの実行中...");
	}
}

const myClassInstance = new MyClass();
myClassInstance.myMethod();
実行結果
~/develop/typescript_decorator$ ts-node methodDecorator.ts
111 {} myMethod {
  value: [Function: myMethod],
  writable: true,
  enumerable: false,
  configurable: true
}
=======22  [Function: myMethod]
メソッド myMethod が呼び出されました
メソッドの実行中...

アクセサリーデコレーター

getterまたはsetterのアクセサリに対して適用されるデコレーターです。

function Enumerable(isEnumerable: boolean) {
	// biome-ignore lint/suspicious/noExplicitAny: This is needed to allow any type for the target
	return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
		descriptor.enumerable = isEnumerable;
	};
}

class MyClass {
	private _value = 0;

	@Enumerable(false)
	get value(): number {
		return this._value;
	}

	set value(val: number) {
		this._value = val;
	}
}

const myClassInstance = new MyClass();
console.log(Object.keys(myClassInstance)); // 'value' は表示されない

プロパティデコレーター

プロパティに対して適用されるデコレーターです。

propertyDecorator.ts
// biome-ignore lint/suspicious/noExplicitAny: This is needed to allow any type for the target
function ReadOnly(target: any, propertyKey: string) {
	Object.defineProperty(target, propertyKey, {
		writable: false,
	});
}

class MyClass {
	@ReadOnly
	name = "Initial name";
}

const myClassInstance = new MyClass();
try {
	myClassInstance.name = "Changed name"; // エラー: nameは読み取り専用です
} catch (error) {
	// biome-ignore lint/suspicious/noExplicitAny: This is needed to handle any type of error
	console.error((error as any).message);
}

実行結果
~/develop/typescript_decorator$ ts-node propertyDecorator.ts
/Users/kawamurakouji/develop/typescript_decorator/propertyDecorator.ts:10
        name = "Initial name";
     ^
TypeError: Cannot assign to read only property 'name' of object '#<MyClass>'
    at new MyClass (/Users/kawamurakouji/develop/typescript_decorator/propertyDecorator.ts:10:6)
    at Object.<anonymous> (/Users/kawamurakouji/develop/typescript_decorator/propertyDecorator.ts:13:25)
    at Module._compile (node:internal/modules/cjs/loader:1369:14)
    at Module.m._compile (/Users/kawamurakouji/.config/yarn/global/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1427:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/kawamurakouji/.config/yarn/global/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1206:32)
    at Function.Module._load (node:internal/modules/cjs/loader:1022:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)
    at phase4 (/Users/kawamurakouji/.config/yarn/global/node_modules/ts-node/src/bin.ts:649:14)

パラメーターデコレーター

メソッドのパラメータに対して適用されるデコレーターです。

parameterDecorator.ts
// biome-ignore lint/suspicious/noExplicitAny: This is needed to allow any type for the target
function LogParameter(target: any, propertyKey: string, parameterIndex: number) {
    const metadataKey = `log_${propertyKey}_parameters`;
    if (Array.isArray(target[metadataKey])) {
      target[metadataKey].push(parameterIndex);
    } else {
      target[metadataKey] = [parameterIndex];
    }
  }
  
  class MyClass {
    myMethod(@LogParameter message: string) {
      console.log(message);
    }
  }
  
  const myClassInstance = new MyClass();
  myClassInstance.myMethod('Hello, world!');
  // 出力: Hello, world!
  
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0