⌨️

【TS】公式ドキュメントから学ぶTypeScript(1. 基礎編)

に公開

はじめに

  • ReactやNext.jsでもTypeScriptは欠かせないため、公式ドキュメントで学んだ内容を記事にしており、今回は基礎編になります。
  • 前回の記事も置いておきますので、「TypeScript興味あるけどやったことないよ〜」という方はぜひご覧ください🙌

https://zenn.dev/k__azuki/articles/090b46d78eadcf

TSファイルからJSファイルの生成

TypeScript自体をブラウザは認識することが出来ません。
そのため、TypeScriptをJavaScriptに変換(コンパイル)する必要が出てくるわけです。
この変換には、tscというトランスパイラを使用します。

npm install -g typescript

ここで、以下のようにhello.tsというファイルを作成したとしましょう。

hello.ts
// Greets the world.
console.log("Hello world!");

hello.tsをtsファイルからjsファイルにコンパイルするには、以下コマンドを実行します。

tsc hello.ts

この操作で、hello.jsというファイルが新たに生成されます。

では、tsファイルにエラーがあった場合はどうでしょう?
hello.tsを以下のように書き換えてみましょう。

hello.ts
- // Greets the world.
- console.log("Hello world!");

+ // This is an industrial-grade general-purpose greeter function:
+ function greet(person, date) {
+  console.log(`Hello ${person}, today is ${date}!`);
+ }
 
+ greet("Brendan");

編集後にtscコマンドでコンパイルすると...

Expected 2 arguments, but got 1.

コンソール上でエラーがあると知らせてくれます。

ただし、エラーが見つかってもそのままjsファイルを生成されてしまうようです。
これは嬉しくないですよね...。
コンパイル時にエラーがあった時にjsファイルを生成しない方法として、tscコマンド実行時にオプション--noEmitOnErrorを渡す方法が挙げられます。

tsc --noEmitOnError hello.ts

型の明示

先ほどのhello.tsでは、greet関数の引数がどんな型なのか指定していませんでしたね。
以下のように記載することで、型の指定が可能です。
ついでに、先ほどエラーが出たgreet関数の呼び出しも併せて修正しています。

hello.ts
 // This is an industrial-grade general-purpose greeter function:
- function greet(person, date) {
+ function greet(person: string, date: Date) {
   console.log(`Hello ${person}, today is ${date}!`);
}

- greet("Brendan");
+ greet("Maddison", new Date());

なお、TypeScriptでは必ずしも型を明示しなくても良いとされています。
TypeScriptが、開発者の意図する型と同じものを推測できる場面では、アノテーションをつけない方が可読性が上がるなどの利点があります。
以下のコードでは、文字列を代入していることから明らかにmsgstring型と推測できますね。

let msg = "hello there!";

コンパイル後のコード

先ほどアノテーションを追加したhello.tsのコンパイル後に生成されるhello.jsを見てみましょう。

hello.js
"use strict";
function greet(person, date) {
    console.log("Hello ".concat(person, ", today is ").concat(date.toDateString(), "!"));
}
greet("Maddison", new Date());

型アノテーションがなくなっていますね。
本記事の冒頭でもチラッとお話しましたが、ブラウザはTypeScriptを解釈・実行することが出来ません。
そこで、型チェックといったエラーの有無の確認をコンパイル時に行い、ブラウザが解釈・実行できるJavaScriptファイルへと変更したのです。

もう少しよく見てみると、console.logの中身の文字列も若干異なっていますね。
これは、TypeScriptがコンパイル時に使用するJavaScriptのバージョンにあります。
hello.tsでは、バッククォートで囲む「テンプレート文字列」を使用していますが、この機能を使うことができるJavaScriptのバージョンは ECMAScript 2015 (ES6) です。
一方で、コンパイル後に生成されたhello.jsのバージョンは ECMAScript 5 (ES5) と古いバージョンのため、テンプレート文字列が使えずにコードが微妙に異なっているのです。
このような事象を回避したい場合、tscコマンド実行時にtargetオプションを渡すと良いでしょう。

tsc --target es2015 hello.ts

型チェックの厳しさの調整

型チェックにも色々な種類があり、デフォルトでは全て無効化されています。
strictオプションで全て有効化することもできますし、個別に設定することも可能です。
公式ハンドブックでは、以下2つについて言及されています。

noImplicitAny

関数の引数や変数の初期値がない場合、TypeScriptは型推論を行う事ができる、any型を割り当てます。
このany型は「なんでも代入できる型」であり、通常のJavaScriptの仕様に戻ってしまうわけです。
しかし、そもそもTypeScriptを使用する目的は「型を割り当てることによりエラーを防ぐ」ことであるため、このany型を使用することは推奨されません。
noImplicitAnyを有効にすることで、暗黙的に割り当てられるany型が存在した場合、エラーとして表示してくれるようになります。

strictNullChecks

通常のJavaScriptではnullundefinedはどのような変数にも割り当てることが出来ます。
それ故、想定していない場面で変数にnullundefinedが代入されてしまう事態が頻発します。
このような事象を回避するためstrictNullChecksを有効化することで、型定義でnullundefinedを設定した変数にのみ、nullundefinedを割り当てる事ができるようになります。

補足

tscコマンド実行時のオプションであったり、noImplicitAnystrictNullChacksを有効 / 無効にする設定は、tsconfig.jsonを作成して管理すると良いでしょう。
以下、公式ドキュメントですのでご参考までに...

https://typescriptbook.jp/reference/tsconfig/tsconfig.json-settings

終わりに

型チェックの厳しさも調整する形なのは少し意外でした。
結構設定することが多そうだけど、現場ではどのようにして決めるのかなぁ...
tsconfig.jsonも深掘りしたいし、公式ハンドブック読みきれてないし、てんてこ舞いだ🫠

Discussion