AEのスクリプトをTypeScriptで書く
環境
TypeScript
インストール
Node.jsが入ってる前提で始める。
typescriptはnpm経由で以下のようにすればグローバルにインストールされる。
npm i -g typescript
ナイトリービルド版は、@nextをつけて。
npm i -g typescript@next
今ちょうどTypeScriptの2.0.0のベータ版が出てて、割りと痒いところに手が届くようになったので、それを使いたい場合は、
npm i -g typescript@beta
とすればよい。
tsconfig.json
tsconfig.json
は、TypeScriptをJavaScriptにコンパイルする際のコンパイラオプション等を書いておくファイル。プロジェクトフォルダのルートに置いておくと、コンパイルする際に自動的に見に来てくれる。中身は単純なのだとこんな感じ。
{ "compilerOptions": { "target": "es3", "outFile": "ScriptExample.jsx" }, "files": [ "typings/aftereffects/ae.d.ts", "ScriptExample.ts" ] }
ExtendScriptはes3相当なので、target
はes3固定。後、デフォルトだと当然.js
で出力されるので、outFileで.jsx
で出力するようにすれば楽。
型定義ファイル
型の恩恵を受けられないとTypeScriptを使う必要がない。型の恩恵を受けるには型定義ファイルが必要だ。 ということで、https://github.com/atarabi/aftereffects.d.tsから型定義ファイルを拾ってこよう。
Visual Studio Code
TypeScriptでの開発はVS Codeが便利。Visual Studio Codeからダウンロード出来る。
使用するTypeScriptを切り替える
デフォルトだとVS Codeに内蔵されたTypeScriptが使用されるので、それを変更する。
Ctrl+Shift+Pを押すとコマンドパレットが開くので、そこでPreferences: Open User Settingsを探す。
選択すると、settings.json
が開かれるので、そこでtypescript.tsdk
の項を追加し使用したいバージョンのTypeScriptのフォルダを指定する。
"typescript.tsdk": "C:/Users/ユーザー名/AppData/Roaming/npm/node_modules/typescript/lib"
のような感じになるはず。
タスクランナー
VS Codeには、ビルド等のタスクをエディタ内から楽に実行出来るタスクランナーという機能がある。
この機能を使ってTypeScriptをwatchモードでビルドすることを考える。
Ctrl+Shift+Pを押してコマンドパレットを開き、Tasks: Configure Task Runnerを選択すると、いくつか項目が出るのでTypeScript - Watch Modeを選ぶ。
すると.vscode
以下にtask.json
が生成される。これで、コマンドパレットからTasks: Run Build Taskを選択するか、Ctrl+Shift+Bを押すと
さきほど設定したタスクが実行され、tsconfig.json
の内容に基づき、watchモードでファイル保存のたびに自動でコンパイルしてくれるようになる。
こんな感じだろうか
TypeScript 2.0環境前提。ae-typescript-example
TypeScriptその他
基本的な文法以外。
type guard、型推論まわり
TypeScript 2.0でタイプガード周りが向上したので、下記のように早期リターンがしやすくなった。
(() => { const comp = app.project.activeItem; if (!(comp instanceof CompItem)) { return; } comp;//上のif文でCompItem以外はreturnしたので、ここはCompItemと推測してくれる!今まではItemと認識されていた。 })();
プロパティに関してもタイプガードしてくれるようになった。
if (app.project.activeItem instanceof CompItem) { const comp = app.project.activeItem;//CompItemと認識してくれる! }
クラスの判定
ExtendScriptにおけるサブクラスといった概念は、JavaScript的に継承してるのではなく、上位と同じメソッド、プロパティを持ってる程度でしかないので 例えばあるレイヤーがAVLayerかどうかをチェックしたい場合は、
if (layer instanceof AVLayer || layer instanceof TextLayer || layer instanceof ShapeLayer) { //layer is AVLayer!! }
のように、AVLayerとそのサブクラスを羅列する必要がある。これを関数化した場合、
function isAVLayer(layer: Layer) { return layer instanceof AVLayer || layer instanceof TextLayer || layer instanceof ShapeLayer; }
のようになるわけだが、これだと型周りの情報が抜けてtrueの場合にAVLayerであると認識してくれない。
if (isAVLayer(layer)) { layer;//Layerクラス!! }
そこで、下記のようにtrueの場合layerの型はAVLayerですよ!と書くと、
function isAVLayer(layer: Layer): layer is AVLayer { return layer instanceof AVLayer || layer instanceof TextLayer || layer instanceof ShapeLayer; }
きちんと解釈してくれる。
if (isAVLayer(layer)) { layer;//AVLayerクラス!! }
polyfill
TypeScriptはes5のpolyfillは用意してくれない(Startupにpolyfillをデフォルトで突っ込んでくれればいいと思うけど)。 for-of文は使えるので、配列周りで面倒なことはそこまでないと思う。
const comp = app.project.activeItem; if (!(comp instanceof CompItem)) { return; } const layers = comp.selectedLayers; const shape_layers: ShapeLayer[] = []; for (let layer of layers) { if (layer instanceof ShapeLayer) { shape_layers.push(layer); } }
es6
上記のfor-of文、let, constや、クラス、分割代入、テンプレート文字列等、es6の機能は普通に使えるのでどんどん使っていけばいいと思う。
const comp = app.project.activeItem; if (!(comp instanceof CompItem)) { return; } const {name, width, height, pixelAspect, bgColor} = comp; const [red, green, blue] = bgColor; comp.layers.addSolid([1 - red, 1 - green, 1 - blue], `invert of "${name}"'s bgColor'`, width, height, pixelAspect);