TenjinIDLとは言え、既存の構文とメカニズムをそのまま使う。それはTypeScript型システム。interfaceとtype alias。
この記事ではJSONスキーマ的に使うインターフェイス、事例はhttps://js.studio-kingdom.com/typescript/handbook/interfacesから:
interface Person { name : string; age? : number; }
- プロパティ名と型表現〈type expression〉でプロパティ宣言
- 区切り記号がカンマではないので注意。宣言終端記号としてセミコロン。
- 省略可能なら ? を付ける。
interface Point { readonly x : number; readonly y : number; color? : Color; }
- 読み込み専用なら readonly
interface DecoratedPoint { readonly x : number; readonly y : number; color? : Color; [propName: string]: any; }
- 残余プロパティに関しては、
[propName: string] : 型表現
で行う。ブラケットは特殊な表現でよく使用される。 * : any;
と書けたらいいが、そうは書けない。
interface SearchFunc { (source: string, subString: string): boolean; }
- 関数のプロファイル〈プロトタイプ | シグネチャ〉を記述できる。スキーマとしてはあまり使わないだろうが。
- このインターフェイスの実装インスタンス(実装クラスのインスタンスオブジェクト)は単一の関数でよい。
- 関数とは callable object。
interface StringArray { [index: number]: string; }
- JavaScript/TypeScriptだと、オブジェクトと配列の区別は微妙で、どちらもマップ型になっている。マップキーを「プロパティ名〈propName〉」と呼ぶか「インデックス〈index〉」と呼ぶかの違い。
- マップ型とは、実際に写像を表現する。写像の域がプロパティ名/インデックスの集合、余域を型定義の一部として宣言する。
- 上記の例だと StringArray : number → string in Set という宣言と同じ。マップ型のマップは正しいし、アロー型と同値。
interface ReadonlyStringArray { readonly [index: number]: string; readonly length: number; }
- readonly も使える。イミュータブルになる。
- これが「歯抜けにならない」とか「長さ以上の部分はundefined」とかは明示されないコンベンションになる。
interface ClockInterface { currentTime: Date; setTime(d: Date); }
- プロパティ/アイテム(インデックスでアクセスできる値)だけでなく、メソッドも宣言できる。
- メソッド宣言は、メソッド定義のヘッドの部分と同じ。
- 戻り値型 void は省略可能なのかな。
- setTime : (d:Date) => void; との差は不明。
interface ClockConstructor { new (hour: number, minute: number); }
- クラスのコンストラクタに対して、そのプロファイルを宣言できる。
- スキーマ言語としては使わないが興味深い。
interface Counter { (start: number): string; interval: number; reset(): void; }
- (start: number): string; の宣言から、このインターフェイスの実装インスタンスは callable object になる。
- callable なので関数だが、オブジェクトとしてのプロパティとメソッドも持つ。
- JavaScriptの関数は、関数であると同時にオブジェクトで、型システム内ではオブジェクト型のデータとして扱われる。
- スキーマ言語としては使わないが興味深い。