TypeScript as constで型の拡大を防ぐ方法
問題
定数オブジェクトを定義しても、TypeScriptが型を広く推論してしまいます。
const STATUS = {
PENDING: 'pending',
ACTIVE: 'active',
CLOSED: 'closed',
};
// 型: { PENDING: string, ACTIVE: string, CLOSED: string }
// 'pending' | 'active' | 'closed'ではなく、ただのstring
STATUS.PENDINGがstring型になるため、'pending' | 'active' | 'closed'を期待する関数パラメータで型エラーが発生します。
解決方法
as constを付けるだけです。
const STATUS = {
PENDING: 'pending',
ACTIVE: 'active',
CLOSED: 'closed',
} as const;
// 型: { readonly PENDING: 'pending', readonly ACTIVE: 'active', readonly CLOSED: 'closed' }
これでSTATUS.PENDINGはstringではなく'pending'リテラル型になります。ユニオン型も抽出できます:
type StatusType = typeof STATUS[keyof typeof STATUS];
// 'pending' | 'active' | 'closed'
function updateStatus(status: StatusType) {
// statusは正確に3つの値のいずれかのみ受け付けます
}
updateStatus(STATUS.ACTIVE); // OK
updateStatus('random'); // コンパイルエラー
配列にも使えます:
const ROLES = ['admin', 'editor', 'viewer'] as const;
type Role = typeof ROLES[number]; // 'admin' | 'editor' | 'viewer'
// string[]ではなくタプルとして推論されます
// readonly ['admin', 'editor', 'viewer']
関数の戻り値にも便利です:
function getConfig() {
return {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3,
} as const;
}
// retriesの型はnumberではなく3
ポイント
as constはすべてのプロパティをreadonly+リテラル型にして、型の拡大を防ぎますtypeof+keyofでユニオン型を抽出すれば、enumなしで同じ効果が得られます- ランタイムオーバーヘッドがゼロで、enumよりもツリーシェイキングに優れています