2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

TypeScriptのDecoratorで画像組み込み

Last updated at Posted at 2015-07-04

TypeScriptのv1.5から Decorator (デコレータ)の仕組みが入った。

将来のES7に入る予定になっていて、AngularJSなどで活用されているらしい。似たような仕組みはJavaやC#, Flex2/ActionScript3にもある。

何ができるのかは分かったがAngularJSの例をのぞくと具体的な活用例が浮かばなかった。そこで考えてみたのが次の画像読み込みの例である。

コード事例

(Embed.ts)Decoratorで画像組み込み
function embed(path:string) {
	"use strict";
	return (target:Object, propKey:string)=>{
		target[propKey] = document.createElement("img");
		let img = <HTMLImageElement>target[propKey];
		img.src = path;
		img.alt = "decorator image";
		img.width = 640;
		img.height = 480;
	};
}

class Main{
	@embed("dog-279698_640.jpg")
	image:HTMLImageElement;
	
	constructor(){
		var d = document
				.getElementById("body")
				.appendChild(this.image);
	}
}

var m = new Main();
<!DOCTYPE html>
<html>
	<head>
		<title>Embed Test</title>
	</head>
<body id="body">
	<script src="Embed.js"></script>
</body>
</html>

% tsc src/Embed.ts -out src/Embed.js -t "ES5" でコンパイルして表示した結果が下。

スクリーンショット 2015-07-04 17.39.47.png

解説

Mainクラスのプロパティimage@embedデコレータで画像を組み込んでいる。@embedの引数に画像のパスを渡しておくと、imageプロパティはHTMLImageElement,つまりimg要素としてDOMで扱えるノードになっている。

@embedデコレータの中身はグローバルに置かれたembed関数である。やっていることは簡単で、createElementでimg要素を作って返している。画像の"組み込み"と書いたが、単に読み込んでいるだけである。

元ネタ

元ネタはFlex2/ActionScript3にあったEmbedメタデータタグ。

[Embed(source='srcimage.png')]
const MyImage:Class;
stage.addChild(new MyImage);

元ネタの方はswfフォーマットの中にバイナリとして組み込み、クラスにひもづくデータとして持つ事ができた。使用する際にはクラスのインスタンスとして使うので同じ画像をたくさん表示する場合にも便利だった。

今回のTypeScript版もプロパティではなく元ネタと同じくクラスデコレータにしようと思ったが、うまくいかなかったのであきらめた。

やりたかったクラスのデコレータ
@embed("dog-279698_640.jpg")
class EmbededClass{}

もうすこし使いやすくする

function embed(
	path:string,
	width:number=640,
	height:number=480,
	alt:string="decorator image"
) {
	"use strict";
	return (target:Object, propKey:string)=>{
		target[propKey] = document.createElement("img");
		let img = <HTMLImageElement>target[propKey];
		img.src = path;
		img.alt = alt;
		img.width = width;
		img.height = height;
	};
}

embedの引数でもう少し設定できるようにしておくと汎用的に使えるようになる。

使うとき
@embed("dog.png",100,100,"可愛い子犬")
imageDog:HTMLImageElement;
@embed("cat.png",200,150,"もふもふ子猫")
imageCat:HTMLImageElement;

思いつくDecoratorのメリット

今回の例は画像を表示するだけなので、画像を返す関数をつくってimage = embed("src.png");とこれまでのようにプロパティに代入していく方法と比べ,コード量では同じかむしろ増えている。

だが、Decoratorを使う事で、ぱっと見、プロパティに 情報をちょっと追加しているだけに見えるようになる。これはコードの量が多くなればなるにつれて効いてくる。

今回の事例は24行の短いコード、やることも画像を表示と明確である。これが数百・数千行、やることもたくさん、となってくると追いかけるのも大変である。

AngularJSや今回の元ネタのActionScript3のFlex2は巨大なフレームワークで、こういったものは大抵、 『お約束』で書かなければならないコードがある。お約束コードを書いていると本来自分の書きたい・やりたい処理の部分が『お約束』に埋もれて見通しが悪くなる。

Decoratorはこういった、 ライブラリ・フレームワークの使用者が知らなくてもいいコードをある意味隠蔽 し、 やるべきことに集中させてくれる メリットがある。

今回の例も、『お約束』のDOM周りを意識しないで書きたい事に集中させてくれてる!と言えなくもない(constructorの中で思いっきりappendChildしているのは気にしない!メソッドのデコレータでうまく意識させないようにできるかも?)。

巨大なライブラリやフレームワークを使わない時でも、複数人や開発したり、あるいは大昔の自分と一緒にコードを書く場合にも似たようなメリットがある。

  • まとめ
    • 情報をちょっと追加しているだけに見えるようになる
    • ライブラリ・フレームワークの使用者が知らなくてもいいお約束をうまく隠してくれる
    • なので本来やりたい事に集中できる
    • 他人や大昔の自分と一緒にコードを書く時にも便利

注意点(v1.5.0-beta)

TypeScript 1.5.0-betaの場合、吐き出すJSのコードが"use strict";をつけてstrictモードで実行するとエラーになる。

githubの最新版ではなおっているとのことなので,実用で使う場合はそちらにしておいた方がいいと思われる。

追記:v1.5.3以降は--experimentalDecoratorsを有効にする必要あり

正式リリースされたv1.5.3以降ではコンパイルオプションで--experimentalDecoratorsを有効にする必要がある。

2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?