RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

【TypeScript入門】JavaScriptとの大きな違い

こんにちは!気づけばラクス入社5年目の aa_crying です。
本日は、現在学習中のTypeScriptの基礎についてお話しできればと思います。

現在ラクス開発部では、部署間での技術知識共有のためTypeScriptの勉強会(読書会)を開催しています。
TypeScriptを先行して導入した部署から、導入を検討している部署向けに知識を展開することが目的です。
まずは指定されたページを読み、その後に読んで理解した内容やわからなかったことを周りのメンバーと議論する形をとっています。

勉強会で読んでいる書籍はこちらになります。


www.amazon.co.jp

今回は、勉強会で得たTypeScriptの入門知識を皆さんに共有できればと思い、記事にさせていただきました。

アジェンダは以下の通りです。

TypeScriptとは

TypeScriptは、Microsoft が開発したJavaScriptに静的型付け機能が追加された言語です。
JavaScriptとは異なる言語ではなく、型に関すること以外のコードについては通常のJavaScriptと同様の構文を使って記述することができます。
TypeScriptはJavaScriptの機能をすべて利用できることから、"スーパーセット"と呼ばれます。
型の異なる関数呼び出しや代入をコンパイル時に検出することによりプログラムの品質を高めることができます。
TypeScriptファイル(.ts)をそのまま実行することはできず、JavaScript ファイル(.js)にコンパイルする必要があります。

TypeScriptのインストール

Node.jsとnpmがインストールされている環境でnpmコマンドでインストールを実施します。

$ npm install -g typescript
# Node.js, npmがインストールされていることが前提

$ tsc -v
Version 4.5.4

tsc -v でバージョンが返ってくればインストールは完了です。
指定したバージョンをインストールしたい場合はtypescript@4.5.4のように@の後ろにバージョンを指定してください。

トランスパイル

先ほどもお伝えした通り、TypeScriptファイル(.ts) をそのまま実行することはできず、JavaScript ファイル(.js)にコンパイルする必要があります。
この作業をトランスパイルと呼びます。

内容としては簡単で、作成したTypeScriptファイル(*.ts)に対してtscコマンドを実行するだけです。
ディレクトリ直下のすべてに対して実施したい場合は、*.tsを指定すれば可能です。

$ tsc sample.ts 
# sample.js ファイルが作られます。

$ tsc *.ts

TS Playground

ここまでTypeScriptの導入方法・実行方法を記載しましたが、TypeScriptでコードを書いて動作を確認したいだけであれば、Microsoftが公式ページで公開している「TS Playground」を使用することをお勧めします。
(※トランスパイルも非同期でやってくれ、「.JS」タブで内容を確認できます。)

www.typescriptlang.org

TypeScript入門

準備が整ったので、入門編開始です。
勉強会(読書会)では、JavaScriptとTypeScriptの違いにフォーカスし型の部分を中心に読んでいるため、TypeScriptの基本的な構文の説明等は省き、型の部分に絞ってお伝えできればと思います。

TypeScriptの『型』について

JavaScriptでは型を事前に宣言することがないので誤った型のデータを関数に渡している場合、実行タイミングになってようやくエラーであることがわかります。
しかし、TypeScriptでは型を宣言するだけではなく事前にコンパイルにより型チェックを行うので、型に問題がある場合はコードを動作させる前に問題を把握することができます。
そのため、TypeScriptは「型安全な言語」と呼ばれています。
またエディタによっては型に関する問題がある場合、コードの記述中にエラーメッセージで問題がある箇所を示してくれるのでコード記述中に気付いて修正を行うことができます。

const str: string = "hoge"; //①
const str2 = "hoge" //②

上記①のstring の部分は 型注釈 といい、strがstring型であることを示しています。
型注釈は②のように省略することが可能です。
なぜ省略が可能かというと、TypeScriptには 型推論 という機能があるからです。

型推論

TypeScriptには周辺情報や文脈から自動的に型を推測してくれる機能があります。
これを 型推論 と呼びます。

以下に簡単な例を示します。

let x = 100;
let str = "aaa";

x = "bbb" // xはnumber型に推論されるためエラー
str = 200 // strはstring型に推論されるためエラー

想定通りに推論してくれることが多いですが、期待している型と違っているケースもあるため、推論された型が想定通りかはチェックする必要があります。
また、関数の戻り値も関数内の文脈から型推論してくれます。

function getAddtion(x: number, y: number) {
    return x + y;
}

上記では引数2つの型がnumber型で、それらを足し合わせた結果を返す関数であるため、戻り値もnumber型に推論されます。
以下のように関数内の条件によって戻り値の型が変わる場合は、Union型で推論してくれます。

function numberOrString(x: number) {
    if (x < 10) {
        return String(x);
    }

    return x;
}

推論結果

function numberOrString(x: number): string | number

以上のように便利でコード量も削減できるTypeScriptの型推論ですが、
より複雑な関数を書く時は可読性の面でも型推論を使わずに型を明記するようにしたほうが良いのかなと個人的には思います。

型ガード

型推論によって型を判別してくれるのはTypeScriptの便利な機能の1つではありますが、Union型等で2つ以上の型を引数に持つ場合、型の曖昧さは残ります。
これを回避するために、以下のように条件判定で型の絞り込みを行うことが多いです。
このようなコードのことを型ガードと呼びます。

function numCut(num: number | string) {
  if (typeof num === "string") {
    return num.charAt(0);
  } else {
      return String(num).charAt(0);
  }
}

Union型でnumberとstringを引数として受け付けていますが、charAt()はstringに対してしか使用できないため、型ガードをつけずにreturn num.charAt(0);だけ記載するとエラーになります。
そのため、typeof等を使って処理する型を制限する必要があります。
「この型の時だけこの処理を行う」が実現できるのが、型ガードです。

typeofだとプリミティブ型については判定できますが、オブジェクトはobject型であることまでしか判定ができません。
typeof nullの結果がobjectになるという例外的な仕様もあるため要注意です。
(object型:プリミティブ型以外の型は全てobject型に分類される)

便利だけど使わないほうがよい「any」

TypeScriptでは、仕様上定義されてはいますがTypeScriptの型の良さが失われてしまう型や関数が存在するため、その1例を紹介します。

TypeScriptのany型は、どんな型でも代入を許す型です。
プリミティブ型であれオブジェクトであれ何を代入してもエラーになりません。

let value: any;

// 以下どれもコンパイルエラーにならない
value = 1;
value = "string";
value = false;

上記の通り型チェックも行われないため、どんな値を入れてもコンパイルエラーにはなりません。

また、以下のように引数の型注釈を省略した場合、型推論によってanyと判別されるパターンもあります。
toUpperCase()はStringに対して使用できるメソッドですが、name がany型と推論され型チェックを通ってしまっています。
この関数に数値を代入するように記載をしても、any型であるためコンパイルエラーは出ず、実行時エラーとなってしまいます。

function nameToUpper(name) {
                
  console.log(name.toUpperCase());
}
 
nameToUpper(12345);

このケースは型注釈を省略せずに記載するほか、設定ファイル tsconfig.json にて noImplicitAny: trueを設定することで、any型を推論した場合にエラーを表示するようにできるため、設定することをお勧めします。

any型はTypeScriptの特徴である型安全性を度外視した型になるため、書籍でも 『最凶の危険性を誇る機能』 として紹介されており、anyの使用はできるだけ避けるようにとのことです。
しかし、JavaScriptをTypeScriptで書き直す際などはいったんany型で定義して実行できる状態を作ってから型定義していくやり方がよさそうで、本コードとしては残すべきではないにしろ、開発途中では役に立ちそうだなと思いました。

補足 「unknown」

同じくどのような値も代入できる型として、unknown型があります。
unknown型は代入はなんでも可能ですが、メソッドやプロパティを使おうとすると、Object is of type 'unknown'.というエラー文言が出て使用できません。

function nameToUpper(name: unknown) {
                
  console.log(name.toUpperCase());
}
 
nameToUpper(1);

こちらはanyを使うより安全ではありますが、メソッドやプロパティを使用できないため扱いづらいという印象が強いです。

おわりに

以上、TypeScriptの入門として主に『型』に関する内容を記載させていただきました。
これからTypeScriptの学習を考えている方の支えになれば幸いです。
勉強会はまだ続いていくので、また機会があれば得た知識を共有できればと思います。


◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.