[[Microsoft]]の[[TypeScript]]プロジェクトWikiの1項目。パフォーマンスに関する重要事項がまとめられている。
## Writing Easy-to-Compile Code
> [!caution]
> すべてのケースで厳守する必要はない。パフォーマンスとそれ以外のトレードオフを考慮して、ケースごとに適切な選択をすること。
### Preferring Interfaces Over Intersections
> [[交差型よりインターフェースの継承を使用する (TypeScript)|交差型よりインターフェースの継承を使用する]]
### 型アノテーションを使う
特に関数の返却型は推論コストがかかる。
#### No Good
```ts
// 返却型を明示しなくても推論可能だが、これが積み重なると推論コストがかかってしまう
function sum(x: number, y: number) {
return x + y;
}
```
#### Good
```ts
function sum(x: number, y: number): number {
return x + y;
}
```
### Preferring Base Types Over Unions
[[ユニオン型 (TypeScript)|ユニオン型]]ではなく[[インターフェース (TypeScript)|インターフェース]]の[[extends (JavaScript)|extends]]でも要件を満たすならそちらの方が高速。ただし、**`drink: Drink` を具象クラスの `Coffee` や `Tea` に[[Narrowing]]できなくなる** ので、`Coffee` や `Tea` 特有の[[プロパティ (TypeScript)|プロパティ]]を意識する必要がある場合は利用できない。
また、選択肢が2~3程度の[[ユニオン型 (TypeScript)|ユニオン型]]ならパフォーマンスの差は誤差なので気にする必要はない。
#### No Good
```ts
interface Coffee {
brand: "Starbucks" | "Doutor" | "Tully's";
type: string;
price: number;
}
interface Tea {
brand: "Twinings" | "Lipton";
flavor: string;
price: number;
}
type Drink = Coffee | Tea;
declare function describeDrink(drink: Drink): string;
```
#### Good
```ts
interface Drink {
brand: "Starbucks" | "Doutor" | "Tully's" | "Twinings" | "Lipton";
price: number;
}
interface Coffee extends Drink {
brand: "Starbucks" | "Doutor" | "Tully's";
type: string;
}
interface Tea extends Drink {
brand: "Twinings" | "Lipton";
flavor: string;
}
declare function describeDrink(drink: Drink): string;
```
### Naming Complex Types
[[型エイリアス (TypeScript)|型エイリアス]]で複雑な型定義を一度行うことによって、型定義のキャッシュを有効活用しパフォーマンスを向上できる。可読性の意味でも中間定義はしておいて損はない。
#### No Good
```ts
// Employee.id が呼びだされるたびに条件付き型が評価される
interface Employee<C> {
id: C extends "N-company" ? `N${string}`
: C extends "S-company" ? `S${string}`
: never;
}
```
#### Good
```ts
// Employee.id: CompanyId<C> が一度呼び出されれば次回以降はCが同じならキャッシュが利用される
type CompanyId<C> = C extends "N-company" ? `N${string}`
: C extends "S-company" ? `S${string}`
: never;
interface Employee<C> {
id: CompanyId<C>;
}
```