はじめに
この記事はTypeScriptのコンパイルに交差型がどれくらい影響を与えるかを調べてみたものとなります。
きっかけはTypeScript公式のPerformance wikiページを読んだことでした。
Interfaces create a single flat object type that detects property conflicts, which are usually important to resolve! Intersections on the other hand just recursively merge properties, and in some cases produce never. Interfaces also display consistently better, whereas type aliases to intersections can't be displayed in part of other intersections. Type relationships between interfaces are also cached, as opposed to intersection types as a whole. A final noteworthy difference is that when checking against a target intersection type, every constituent is checked before checking against the "effective"/"flattened" type.
For this reason, extending types with interfaces/extends is suggested over creating intersection types.
- type Foo = Bar & Baz & { - someProp: string; - } + interface Foo extends Bar, Baz { + someProp: string; + }
そこには「交差型よりもインターフェースを優先する方が、コンパイルパフォーマンスには良い」という記載があったのですが、具体的な数値の記載が見当たりませんでした。
そこで、「実際にはどの程度ちがうのか?」「キャッシュがどれくらいコンパイルパフォーマンスに効いているのか」という疑問が湧いたため、自分で測定してみることにしました。
測定方法
実際にアプリケーション作成で使うような形ではなく、下記のような実証用コードを用いてコンパイルにかかった時間を測定しました。
公式Wikiにも記載があるとおり、コードベースによってコンパイル速度への影響が変わる可能性があるようです。今回は実証用であることを念頭に置いて、記事を読み進んでいただけると嬉しいです。
例:3個の交差型用実証コード
type Type1 = {
prop1_1: string;
};
type Type2 = {
prop2_1: string;
};
type Type3 = {
prop3_1: string;
};
type CombinedType = Type1 & Type2 & Type3;
function testFunction(obj: CombinedType) {
const access1 = obj.prop1_1;
const access2 = obj.prop2_1;
const access3 = obj.prop3_1;
return obj;
}
例:3個のinterface継承実証コード
interface Type1 {
prop1_1: string;
};
interface Type2 {
prop2_1: string;
};
interface Type3 {
prop3_1: string;
};
interface CombinedType extends Type1, Type2, Type3 {}
function testFunction(obj: CombinedType) {
const access1 = obj.prop1_1;
const access2 = obj.prop2_1;
const access3 = obj.prop3_1;
return obj;
}
他筆者が利用したパッケージ群のバージョンも参考までに記載しておきます。
- TypeScript:5.6.0
- @types/node:22.7.0
- Node:20.15.0
比較内容
-
交差させる型・継承する型を増やした時の比較
- 各型のプロパティの数は固定
- 関数内でアクセスするプロパティの数も固定
-
各型のプロパティのみを増やした時の比較
- 交差させる型・継承する型数は固定
- 関数内でアクセスするプロパティの数も固定
-
関数内でアクセスするプロパティのみを増やした時の比較
- 交差させる型・継承する型数は固定
- 各型のプロパティの数は固定
これらをキャッシュあり・なし × type・interfaceの4パターンで、測定対象が、1, 10, 100, 1000, 10000で増加する時の測定をおこないました。いずれも5回測定し、有意差が実際に得られるかも見てみました。
比較結果
交差させる型・継承する型を増やした時を増やした時の結果
| 型数 | 交差型(秒) | インターフェース(秒) | キャッシュ効果 | 性能差 | 統計的評価 |
|---|---|---|---|---|---|
| キャッシュなし→あり | キャッシュなし→あり | 改善倍率 | Interface/Type | 交差型vs Interface差 | |
| 1 | 2.05→2.85 (+0.80秒, 1.39倍) | 1.97→3.03 (+1.06秒, 1.54倍) | Interface有利 | なし: 0.96倍, あり: 1.06倍 | 統計的誤差 |
| 10 | 2.00→2.75 (+0.75秒, 1.37倍) | 2.12→2.88 (+0.76秒, 1.36倍) | 同程度 | なし: 1.06倍, あり: 1.05倍 | 統計的誤差 |
| 100 | 2.14→2.84 (+0.70秒, 1.33倍) | 2.05→2.76 (+0.71秒, 1.35倍) | 同程度 | なし: 0.96倍, あり: 0.97倍 | 統計的誤差 |
| 1000 | 2.86→2.80 (-0.06秒, 0.98倍) | 2.98→2.72 (-0.26秒, 0.91倍) | Interface有利 | なし: 1.04倍, あり: 0.97倍 | 統計的誤差 |
| 10000 | 82.4→12.0 (-70.4秒, 0.15倍) | 157.4→3.5 (-153.9秒, 0.02倍) | Interface圧勝 | なし: 1.91倍, あり: 0.29倍 | 🔥有意差あり |
各型のプロパティのみを増やした時の結果
| プロパティ数 | 交差型(秒) | インターフェース(秒) | キャッシュ効果 | 性能差 | 統計的評価 |
|---|---|---|---|---|---|
| キャッシュなし→あり | キャッシュなし→あり | 改善倍率 | Interface/Type | 交差型vs Interface差 | |
| 1 | 2.59→2.65 (+0.06秒, 1.02倍) | 2.36→2.57 (+0.21秒, 1.09倍) | 同程度 | なし: 0.91倍, あり: 0.97倍 | 統計的誤差 |
| 10 | 2.19→2.63 (+0.44秒, 1.20倍) | 2.47→2.64 (+0.17秒, 1.07倍) | 同程度 | なし: 1.12倍, あり: 1.00倍 | 統計的誤差 |
| 100 | 2.48→2.50 (+0.02秒, 1.01倍) | 2.50→2.60 (+0.10秒, 1.04倍) | 同程度 | なし: 1.01倍, あり: 1.04倍 | 統計的誤差 |
| 1000 | 2.59→2.53 (-0.06秒, 0.98倍) | 2.70→2.61 (-0.09秒, 0.97倍) | 同程度 | なし: 1.04倍, あり: 1.03倍 | 統計的誤差 |
| 10000 | 3.81→3.13 (-0.68秒, 0.82倍) | 4.30→3.42 (-0.88秒, 0.80倍) | 同程度 | なし: 1.13倍, あり: 1.09倍 | 統計的誤差 |
関数内でアクセスするプロパティのみを増やした時の結果
| アクセス数 | 交差型(秒) | インターフェース(秒) | キャッシュ効果 | 性能差 | 統計的評価 |
|---|---|---|---|---|---|
| キャッシュなし→あり | キャッシュなし→あり | 改善倍率 | Interface/Type | 交差型vs Interface差 | |
| 1 | 2.40→2.67 (+0.27秒, 1.11倍) | 2.49→2.45 (-0.04秒, 0.98倍) | 不安定 | なし: 1.04倍, あり: 0.91倍 | 統計的誤差 |
| 10 | 2.67→2.49 (-0.18秒, 0.93倍) | 2.42→2.59 (+0.17秒, 1.07倍) | 不安定 | なし: 0.91倍, あり: 1.04倍 | 統計的誤差 |
| 100 | 2.62→2.50 (-0.12秒, 0.95倍) | 2.58→2.42 (-0.16秒, 0.94倍) | 同程度 | なし: 0.98倍, あり: 0.97倍 | 統計的誤差 |
| 1000 | 2.49→2.43 (-0.06秒, 0.98倍) | 2.58→2.50 (-0.08秒, 0.97倍) | 同程度 | なし: 1.04倍, あり: 1.03倍 | 統計的誤差 |
| 10000 | 2.59→2.48 (-0.11秒, 0.96倍) | 2.54→2.43 (-0.11秒, 0.96倍) | 同程度 | なし: 0.98倍, あり: 0.98倍 | 統計的誤差 |
まとめ
交差型 vs インターフェースで有意差があったケースは、交差させる型・継承する型を増やした時を増やした時の型数が10000のケースのみでした。またキャッシュ自体も交差させる型・継承する型を増やした時を増やした時のみで有効である結果が今回は得られました。
また、各グラフの縦軸を見ていただけると分かるかと思いますが、今回の測定項目の中で最もコンパイル速度に影響を与えるのは交差・継承させる型数であることもわかりました。
その他として、今回の測定範囲では、各型のプロパティのみを増やした時における有意差は得られませんでした。しかしながら、グラフの傾向を見るにプロパティ数をより増加させることで、交差型とインターフェースの間に差がでてくるようになる可能性があります。
さいごに
測定方法の欄でも記載した通り、今回の結果は本検証用に作成したコードのコンパイル速度を算出したものであり、実際のアプリケーション作成で使うようなケースを対象に測定したものではありません。
フレームワークやライブラリを利用することでもまた結果が変わる可能性はあるので、それようにして検証を行ってみるもの面白いかもしれません。


