MARDEV

TypeScriptを勉強しようシリーズ No.01

Udemy講座「understanding typescript」を使用してTypeScriptを勉強していこうという記事です。 今回は「TypeScriptの型」についてです。

第2章 : TypeScript Basics & Basic Types

本章では、「TypeScript基本の型」について学ぶことができます。

ちなみに、いきなり第2章から始まってますが、第1章は環境構築や講座の流れの説明だったのでスキップします。

TypeScriptの型注釈

TypeScriptでは、変数や関数の引数に対して型を指定することができます。

型を指定することで、コンパイル時に型エラーを検知できるほか、可読性も高まります。

例えば下記のように変数xと変数yがあったとします。(実際はこのような変数名は使用しないとは思いますが…)

function add(x, y) {
  return x + y;
}

const x = '5'
const y = 10

const result = add(x, y);

この場合は、変数xには文字列の5が定義されており、変数yには数字の10が定義されています。

変数resultは、510になることがわかりますね。

しかし、開発者はxもyも数字を想定していました。

つまりは 5 + 10 = 15を想定していたというわけです。

開発者以外の人が、変数xの扱いを間違えたためにこのようなミスが発生するのです(今回の例は誇張していますが)

そこでTypeScriptの型注釈を使用します。

関数addの引数に、数字の型である「number」を定義します。

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

}

const x = '5'
const y = 10

const result = add(x, y);

これにより、あとから変数の値を変更する人も、xとyには数字が入る想定なんだなと分かります。

仮に、上記のようにx='5'と定義した場合は、xには文字列は定義できないよというエラーが表示されるので間違いに気づくことができるわけです。

TypeScriptの型の種類

1. number

数値の型。

Javaのように、整数(int)や小数(float)を型レベルで区別せず、一律numberで定義します。

const num1: number = 10;
const num2: number = 2.5;

2. string

文字列の型。

他の言語やJavaScriptと同じように、文字列を""、''、``のいずれかで囲む。

const str1: string = "Cebu"
const str2: string = 'Cebu'
const str3: string = `Cebu`

3. boolean

論理型。

これも他の言語やJavaScriptと同じように、true または false の論理値を格納するための型。

const isChecked: boolean = true;
const isLogin: boolean = false;

4. object

オブジェクト型。

下記はJavaScriptの場合のオブジェクト変数の定義内容です。

const person = {
  name: '田中太郎',
  age: 30
}

これに型注釈をつけると下記のようになります。

const person:  {
  name: string;
  age: number;
} = {
  name: '田中太郎',
  age: 30
}

オブジェクトの型注釈にはobject型を用いることもできます。

しかし、object型の使用はお勧めしません。

上記の記述内容とは異なり、object型に何のプロパティがあるかの情報がないためです。

const person: object = {
  name: '田中太郎',
  age: 30
}

5. Array

配列リテラル(array literal)型。

配列リテラル内の要素の型を定義します。

同じ型の要素のみを格納できます。

配列の長さが動的に変わります。

const vegetables: string = ['cabbage', 'eggplant', 'lettuce']
const age: number = [25, 30, 35]

// string型注釈を定義しているのに、配列にnumber型の要素をいれるとエラーになります。
const vegetables: string = ['cabbage', 2, 'lettuce']

6. Tuple

JavaScriptには存在しません。

異なる型の要素を格納できます。

配列の長さが固定されます(下記の例では[string, number, string]としているので、配列の長さは3)。

function getAnswer(num: number): [string, number, string] {
    const answer = num * 2;
    return [`${num}を2倍すると、`, answer, 'になります。'];
}

const message: [string, number, string] = getAnswer(2)
console.log(message)

// 出力結果
// [LOG]: ["2を2倍すると、", 4, "になります。"] 

tupleで定義した変数は、タプルで定義した範囲外の要素に対してアクセスができません。

例えば、message.push('test')として、配列の4番目に値を追加したとします。

下記のように4番目の要素をログ出力しようとしてもエラーが発生します。

ただし、要素にアクセスできないだけで、配列の4番目にはpushした「test」は存在します。


7. Enum

列挙型。

JavaScriptには存在しません。

enum Role {
    Student,
    Teacher,
    Manager,
}

const Me: {
    name: string,
    age: number,
    role: String,
} = {
    name: 'mardev',
    age: 15,
    role: Role[0],
}

console.log(Me) 

// 出力結果
// [LOG]: {
//   "name": "mardev",
//   "age": 15,
//   "role": "Student"
// } 

しかし、TypeScriptの列挙型(enum)にはいくつか問題点が指摘されているみたいです。

詳細はサバイバルTypeScript に詳しく書かれておりましたのでこちらをご参照ください


8. any

どのような型にもなることができる型。

コンパイラーが型チェックを行わないため、どのような値を代入してもエラーになりません。

TypeScriptで型を省略した場合も、暗黙的にany型として扱われます。

TypeScriptの良さである型チェックが無視されてしまうので、どうしてもanyを使わないとならない場面以外は使わないほうがいいでしょう。

// 変数[test1]に文字列を代入後、数字を再代入(エラーなし)
let test1: any = 'test';
test1 = 1;

9. Union

いずれかの型」を表現する型。

2つ以上の型をパイプ記号(|)で繋げて書き、いずれかの型を満たせばOKとなる。

// string型、number型、boolean型の値を保持できるUnionType
type UnionType = string | number | boolean;

//UnionType 型のパラメータを受け取り、ログ出力
function displayValue(value: UnionType): void {
    console.log(value);
}

// string型、number型、boolean型の値を引数として、関数「displayValue」を呼び出し
displayValue("Hello");    // string型
displayValue(42);         // number型
displayValue(true);       // boolean型

// 出力結果
// [LOG]: "Hello" 
// [LOG]: 42 
// [LOG]: true 

10. Literal

プリミティブ型の特定の値だけを代入可能にする型。

主にユニオン型と併用する。

// 'north' または 'south' または 'east' または 'west' のみ許容する
function printDirection(direction: 'north' | 'south' | 'east' | 'west'): void {
    console.log("You are heading " + direction + ".");
}

printDirection('north'); // 出力: "You are heading north."
printDirection('up');    // エラー: 不適切な値

11. Type Aliase

型に独自の名前をつけることができます(=型エイリアス)

type を用いて、型エイリアスを宣言します。

// 冗長なコード: 配列の型を直接指定
let coordinates1: [number, number] = [3, 4];
let coordinates2: [number, number] = [5, 7];
let person1: { name: string, age: number } = { name: 'Alice', age: 30 };
let person2: { name: string, age: number } = { name: 'Bob', age: 25 };

上記は変数1つ1つに型を定義しています。

このような冗長な型注釈は、型エイリアスを使用して解消できます。

// 型エイリアスの定義
type Coordinates = [number, number];
type Person = { name: string, age: number };

// 型エイリアスを使用したコード
let coordinates1: Coordinates = [3, 4];
let coordinates2: Coordinates = [5, 7];
let person1: Person = { name: 'Alice', age: 30 };
let person2: Person = { name: 'Bob', age: 25 };

12. void

returnステートメントがない場合(戻り値がない場合)はvoidを使用する。

function print(message: string): void {
  console.log(message);
}

ただし、void型で戻り値の型注釈をしなくても、型推論によって戻り値の型はvoidとなっていることが下記にて分かります。

ただし、コーダー目線的には、voidを記載することで戻り値がない関数であることが一目でわかるので

戻り値がない関数の場合はvoidを記載するようにしたほうがよさそうですね。


13. 関数の型の宣言(function type declaration)

関数の型を定義する方法の一つです。

関数が受け取る引数の型や、返り値の型を明示的に指定することで、その関数の型を定義します。

// 関数の型の宣言
type AddFunction = (a: number, b: number) => number;

// 関数の定義
const add: AddFunction = (a, b) => {
    return a + b;
};

// 関数の使用
console.log(add(3, 5)); // 出力: 8

使用する場合の例:

コールバック関数の型を明示する場合や、高階関数の引数や返り値の型を明確にする場合などに使用する。

使用するメリット:

関数の型を明示的に宣言することで、関数の引数や返り値の型がどのようであるかを明確にすることができます。

これにより、コードの可読性が向上します。

関数の型を宣言することで、型チェックが行われ、予期せぬ型の不一致によるエラーを事前に防ぐことができます。

型エイリアスやインターフェースと組み合わせて使うことで、複雑な型を再利用しやすくなります。

// コールバック関数の型を明示する
type Callback = (result: number) => void;

// コールバック関数を受け取る関数
function process(callback: Callback): void {
    // 何らかの処理を行った後、結果をコールバック関数に渡す
    const result: number = 42;
    callback(result);
}

// コールバック関数を定義
const callbackFunction: Callback = (result) => {
    console.log("処理の結果:", result);
};

// process関数にコールバック関数を渡す
process(callbackFunction);
// 高階関数の引数や返り値の型を明示する
type HigherOrderFunction = (callback: (result: number) => void) => number;

// 高階関数を定義
const higherOrderFunction: HigherOrderFunction = (callback) => {

    // 何らかの処理を行った後、結果をコールバック関数に渡す
    const result: number = 42;
    callback(result);

    // 高階関数の返り値を返す
    return result;

};

// コールバック関数を定義
const callbackFunction = (result: number) => {
    console.log("処理の結果:", result);
};

// 高階関数を呼び出し
const returnValue = higherOrderFunction(callbackFunction);
console.log("高階関数の返り値:", returnValue);

14. コールバック関数

他の関数の引数として渡され、ある特定のイベントが発生した際やある条件が満たされた際に、その関数が呼び出される関数のことです。

非同期処理やイベント駆動型のプログラミングにおいてよく使用されます。

// コールバック関数を受け取る関数
function process(callback: (result: number) => void): void {
    // 何らかの処理を行った後、結果をコールバック関数に渡す
    const result: number = 42;
    callback(result);
}

// コールバック関数を定義
function callbackFunction(result: number): void {
    console.log("処理の結果:", result);
}

// コールバック関数を渡して関数を呼び出し
process(callbackFunction);

使用する場合の例:

  • 非同期処理: 非同期関数やイベントリスナーに対して、処理が完了した後に行うべきアクションを指定する際に使用されます。
  • カスタムイベント処理: 特定のイベントが発生したときに実行する処理を指定するために使用されます。
  • 高階関数: 高階関数の引数として渡され、その関数が呼び出される際に、特定の処理を行うために使用されます。

使用するメリット:

  • 柔軟性: コールバック関数を使うことで、異なる処理を同じ関数に渡すことができ、関数の再利用性が高まります。
  • 非同期処理: 非同期処理の完了時に実行する処理を指定するために使用され、非同期処理の制御が容易になります。
  • 分離: 処理の分離が容易であり、コードがより読みやすくなります。

15. unknown

unknown型は、TypeScriptにおいて、他の型に代入可能なすべての値の型を表す型です。

つまり、unknown型は「不明な型」を示します。

any型と似ていますが、unknown型は型安全性を保持します。

unknown型の変数は、その型が不明なため、そのままでは特定の型の値を参照することができません。

型の安全性を維持しながら、型が不明な値を扱う場合に使用されます。

// unknown型の変数宣言
let userInput: unknown;

// string型への代入
userInput = 5;

// 又は↓のようにnumber型への代入
userInput = 'Hello';

// unknown型の値を使用する
let userName: string;

// タイプガードを使用して型の確認
if (typeof userInput === 'string') {
    userName = userInput; // ここでは問題なし
} else {
    userName = 'Default Name'; // userInputがstring型でない場合のデフォルト値
}

console.log(userName); // 出力: 'Hello' もしくは 'Default Name'

使用する場合の例:

  • 動的なデータの扱い: ユーザー入力やAPIのレスポンスなど、実行時に型が明確でないデータを扱う際に使用されます。
  • 外部ライブラリやフレームワークとの統合: 外部からのデータの型が確定していない場合に使用されます。

16. never

決して発生しない値を持つ型です。

具体的には、次のような場合にnever型が使用されます。

  • 関数が例外をスローしている場合。
  • 無限ループに陥っている場合。
  • returnステートメントを持っていない場合。
// never型を返す関数
function throwError(message: string): never {
    throw new Error(message);
}

// 無限ループを行う関数
function infiniteLoop(): never {
    while (true) {
        console.log("Infinite loop");
    }
}

// never型の変数
let x: never;

// never型の代入
x = throwError("Error occurred");

まとめ

以上が「第2章 : TypeScript Basics & Basic Types」の内容のまとめです。

他の言語を触ったことのある方は見慣れた型も多かったのではないのでしょうか?

第2章ということもあり、まだ取っかかりやすい内容でした。

サバイバルTypeScript ではほかの型も紹介されておりましたので、ぜひそちらも確認してみてください。

第3章は「The TypeScript Compiler (and its Configuration)」になります。

TypeScriptのコンパイラーとその設定についてですね。

最近風邪気味なのでボチボチやっていこうとおもいます。。。