この記事では、TypeScriptにおけるconst type parameterという概念と、それがどのようにas const(constアサーション)の使用を省略できるかについて説明します。これは初心者にとって少し複雑に感じるかもしれませんが、実際には非常に便利で強力な機能です。では、早速見ていきましょう。
基本的な関数の例
まずは、基本的な関数を2つ見てみましょう。
function withoutConst<T>(value: T): T {
return value;
}
function withConst<const T>(value: T): T {
return value;
}
ここで、withoutConstとwithConstの2つの関数があります。これらはどちらも引数をそのまま返す非常にシンプルな関数ですが、重要な違いが一つあります。それは、withConst関数にconst Tという型パラメータが使われている点です。
withoutConstの動作
withoutConst関数を使うと、型はより一般的な形で推論されます:
const value1 = withoutConst(123);
// ^? const value1: 123
const value2 = withoutConst({ num: 123 });
// ^? const value2: { num: number; }
const value3 = withoutConst([1, 2, 3]);
// ^? const value3: number[]
ここでは、数値、オブジェクト、配列の3つの異なる型の値をwithoutConstに渡しています。この場合、TypeScriptはそれぞれの型を推論しますが、オブジェクトや配列はその構成要素が変更可能であると見なされます(つまり、readonlyではありません)。
constアサーションの使用
もし、変更不可能な(immutableな)型を確保したい場合、constアサーションを使うことができます。これはas constと記述します。例えば:
const value1 = withoutConst(123 as const);
// ^? const value1: 123
const value2 = withoutConst({ num: 123 } as const);
// ^? const value2: { readonly num: 123; }
const value3 = withoutConst([1, 2, 3] as const);
// ^? const value3: readonly [1, 2, 3]
ここで、as constを使うことで、TypeScriptに対して値が変更不可能であることを明示しています。これにより、オブジェクトや配列はreadonlyとして扱われます。また、numの型は123のようなリテラル型にもなります。更に、配列は単なる配列型(T[])ではなくタプル型として扱われます。
withConstの威力
const type parameterを使うwithConst関数を使うと、as constを省略できます。つまり、同じ振る舞いをもっと簡単に実現できるのです。
const value1 = withConst(123);
// ^? const value1: 123
const value2 = withConst({ num: 123 });
// ^? const value2: { readonly num: 123; }
const value3 = withConst([1, 2, 3]);
// ^? const value3: readonly [1, 2, 3]
この場合、withConstは自動的にその引数が変更不可能であると推論し、そのためas constを付ける必要がありません。これにより、コードがよりシンプルで読みやすくなります。
まとめ
const type parameterを使用することで、TypeScriptの型システムの強力さをさらに引き出すことができます。特に、as constを多用している場合には、この機能を使ってコードをよりシンプルに保つことができます。
ちなみに、本稿で扱ったサンプルコードはTypeScript Playgroundにあります: