こんにちは。野中やすおです。
今回の記事では、TypeScript 3.4で導入されたconst assertionのas constがとても便利なことに気がついたので記事にしてみます。
TypeScript 3.4 Release Notes…
as constとは
as constは、式の末尾に使用されます。as constを使用することでTypeScriptの型推論に以下の効果をもたらします。
- Wideningを防ぐ(no literal types in that expression should be widened)
- オブジェクトリテラルは、readonlyプロパティになる(object literals get readonly properties)
- 配列リテラルはreadonlyタプルになる(array literals become readonly tuples)
Wideningとは
Wideningは、よくよくみると英単語「Wide」のing形で、直訳すると拡張しているという意味になります。そこから、「リテラル型が自動的にプリミティブ型に広がって推論されること」を指します。
as constの使用例
まずは以下のようなオブジェクトがあるとします。そして末尾にas constを使用します。
| 1 2 3 4 5 | const yasuo = {   name: "yasuo",   age: 30,   gender: "man", } as const; | 
これに
| 1 | yasuo.name = "nonaka"; | 
を代入しようとするとreadonlyのためもちろん以下のようにエラーになります。

それなら各プロパティにreadonlyをつければいいだけじゃない?ってなりますが、const assertionは再帰的にオブジェクトリテラルや配列リテラルをreadonlyにすることができます。
この性質を利用して、例えば以下のような関数TestAがあるとします。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | type DataType = string | number | null; type ColumnType = 'string' | 'number' | 'datetime'; function TestA(data: DataType, type: ColumnType): string {     if (data === null) return '';     switch (type) {         case 'string':             return data as string;         case 'number':             return data.toString();         case 'datetime':             const date = new Date(data as string);             date.setHours(date.getHours() + 9);             const formattedDate = date.toISOString().split('T')[0].replace(/-/g, '/');             const formattedTime = date.toISOString().split('T')[1].slice(0, 5);             return `${formattedDate} ${formattedTime}`;         default:             throw new Error(`Unsupported type: ${type}`);     } } | 
この関数に対して、自分は型エラー回避のために以下のように冗長に型をつけていました。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | type DataType = string | number | null;  type ColumnType = 'string' | 'number' | 'datetime'; describe('TestA', () => { 	test.each([ 		// string test case 		{ 			data: 'Test String', 			type: 'string' as ColumnType, 			expected: 'Test String', 		}, 		// number test case 		{ data: 123, type: 'number' as ColumnType, expected: '123' }, 		// datetime test case 		{ 			data: '2021-12-31T00:00:00Z', 			type: 'datetime' as ColumnType, 			expected: '2021/12/31 09:00', 		}, 		// null data test case 		{ data: null, type: 'string' <span class="crayon-st">as</span> <span class="crayon-v">ColumnType</span><span class="crayon-sy">,</span>, expected: '' }, 		{ data: null, type: 'number' as ColumnType, expected: '' }, 		{ data: null, type: 'datetime' as ColumnType, expected: '' }, 	])('TestA($data, $type) should return $expected', ({ data, type, expected }) => { 		expect(TestA(data, type)).toEqual(expected) 	}) }) | 
しかしas const を使用してテストケースのデータを修正すると、ColumnType へのキャスト (as ColumnType) の必要がなくなります。
よって以下のようになります。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | describe('TestA', () => { 	test.each([ 		// string test case 		{ 			data: 'Test String', 			type: 'string', 			expected: 'Test String', 		}, 		// number test case 		{ data: 123, type: 'number', expected: '123' }, 		// datetime test case 		{ 			data: '2021-12-31T00:00:00Z', 			type: 'datetime', 			expected: '2021/12/31 09:00', 		}, 		// null data test case 		{ data: null, type: 'string', expected: '' }, 		{ data: null, type: 'number', expected: '' }, 		{ data: null, type: 'datetime', expected: '' }, 	] as const)('TestA($data, $type) should return $expected', ({ data, type, expected }) => { 		expect(TestA(data, type)).toEqual(expected); 	}); }); | 
かなりテストがすっきりしたかと思います。 as constはテストケースで使用するのにも非常に有用であることを学びました。
参考
変数宣言のときに、末尾にas constをつけるとその値をreadonlyにした上で、リテラル型にしてくれます。…

.png?pattern=cross&md=0&fontSize=75px&textColor=%23ffffff&textStrongColor=%238340BB&overlay=https%3A%2F%2Fraw.githubusercontent.com%2Fyytypescript%2Fog-image%2Fmain%2Fpublic%2Fogp-overlay.svg)
 
						
						 
												