TypeScript v4.1 で導入された機能のお話

この記事は「[イエソド アウトプット筋 トレーニング Advent Calendar 2020](https://adventar.org/calendars/5888)」7日目の記事です。

短時間でさっと書いて、アウトプット力を少しでも高めることが目標なので、最近リリースされたTypeScriptの新バージョンについてさっと書いてみます。


# TypeScript Version

本記事の内容は、2020年11月にリリースされた[TypeScript4.1.2](https://github.com/microsoft/TypeScript/releases/tag/v4.1.2)の新機能についてです。

```shell
$ npm search typescript
NAME                      | DESCRIPTION          | AUTHOR          | DATE       | VERSION  | KEYWORDS                                         
typescript                | TypeScript is a…     | =typescript-de… | 2020-11-19 | 4.1.2    | TypeScript Microsoft compiler language javascript
```

ローカルで実際に確認したい場合は、

```shell
$ mkdir ts-4.1 && cd $_
$ npm init -y
$ npm i -D typescript
$ npx tsc --init
$ tree -L 2

.
├── node_modules
│   └── typescript
├── package-lock.json
├── package.json
└── tsconfig.json
```

で必要最低限の環境で確認できます。終わったらディレクトリ削除しちゃいましょう。(各種binがある前提)

vscodeで確認する場合は、vscodeが使用するTypeScriptのversionをprojectでインストールされたTypeScirptに合わせられるよう設定する必要があるので注意。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271259/2064772b-5d61-5a82-ed0c-49c37b8a9b77.png)

あるいは、以下をプロジェクトルートに配置すればおkです。

```json:.vscode/settings.json
{
  "typescript.tsdk": "node_modules/typescript/lib"
}
```

そうすれば、vscodeの右下のメニューから変更できるハズ。

![スクリーンショット 2020-12-07 19.12.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271259/9dfad8c5-bd4e-5e32-f4aa-d698a7a7fc56.png)



# [Template Literal Types](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#template-literal-types)

段階を踏んで説明するため、先に以下2つをご紹介。


## [(String) Literal Types](https://typescript-jp.gitbook.io/deep-dive/type-system/literal-types)

TypeScirptでは、文字列リテラルを(文字列に問わず 数値/真偽値/オブジェクト でも)型として使用することができます。

```typescript:文字列リテラル型
type Status = 'success' | 'error';

const status1: Status = 'success';
let status2: Status = 'success';
status2 = 'error';

const errStatus: Status = 'warn'; // error
```


## [Template Strings](https://basarat.gitbook.io/typescript/future-javascript/template-strings)

通常の文字列リテラルの記述は`'`(シングルクォーテーション)または`"`(ダブルクォーテーション)で書くことが多いと思います。
一方で、文字列内に変数をバインドしたい場合や、複数行に渡る文字列を宣言したい場合、バッククォーテーションで書くと非常にシンプルになります。

```typescript:テンプレートリテラル
function hello(name: string) {
  console.log(`Hello, ${name}`);
}

const name = 'swallowtail62';
hello(name); // `Hello, swallowtail62`という文字列がコンソール出力される
```

---

Template Literal Typesは上記の2つの機能を組み合わせたようなものだとイメージしてもらえば分かりやすい？と個人的に思っています。

```typescript
type Status = `Success` | `Error`; // テンプレート文字列リテラルでの型定義は、v4.1以前からOK (Qiita上ではこのsyntax errorに見えちゃうけど)
type Result = `status: ${Status}`;　// 
```

こんな風に文字列リテラル内に別の型をバインドするようなことが出来ちゃいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271259/fccae2f0-d689-d035-e28f-bdb162e04005.png)


これが便利だと言われてて、プレフィックスなりサフィックスなり何らか特定の文字を含んだ文字列を型としてバリデーションできたりしちゃいます。

```typescript
type Prefix = `YSD-`;
type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type TicketId = `${Prefix}${Digit}`;

const ticketId: TicketId = 'YSD-0'
```

番号が1桁の場合には、上記の`TicketId`で型を定義できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271259/c4bd90b4-582d-aa1c-45fa-d4720f31832a.png)

ただ、現実的にチケット番号が1桁で収まるハズはなく、正規表現のように

```typescript
type TicketId = `${Prefix}${Digit}+`;
```
みたいな感じで定義できればいいんですが、やり方があるのかは分かりません。。

もっと身近に扱えて恩恵を受けられるとすれば、url定義とかでしょうか。

```typescript
type Url = `${'http' | 'https'}://${string}`;

const min: Url = 'http://';
const normal: Url = 'https://qiita.com';

const abnormal1: Url = 'http:/qiita.com';
const abnormal2: Url = 'htpps://qiita.com';
```

`/`が1つ不足していたりプロトコルをタイポしたりというのが防げるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271259/3e44554c-a824-8e3d-0e2a-953fd3060147.png)



## Notes

[TypeScriptのrepositoryにもissueが挙がっている](https://github.com/microsoft/TypeScript/issues/41732)通り、
Template Stringsでバインドしている変数がたとえ厳密な型定義(non-widening)をされていても、その型情報は評価されず、`string`型として評価されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271259/3c9e7e01-0bd2-ea00-98f1-12b256dc4ce8.png)

上記画像の例で言うと、

```typescript

const domain = `google.com`; // type: "google.com"
```

なので、

```typescript

const googleSearch = `https://${domain}/search`;
```

は、`type: "https://google.com/search"`と型評価されて欲しいところですが、実際には`string`となります。


---

本当は他の機能についても書くつもりでしたが、既に30minを軽くオーバーしてしまっているため、一旦終わり。
時間と気力があれば続きを書きます。