🗃️

TypeScriptの勉強 2 ~型について~

2022/02/21に公開約5,500字

初めに

この記事は、「TypeScript の勉強 1 ~導入から実行まで~」の続きです。

一つ前の記事はこちらを参照してください。

https://zenn.dev/masa_dev/articles/30d92a3ed990dae73672

実行環境

今回は、tscコマンドを用いて TypeScript をコンパイルします。

tscコマンドを用いるためには、typescriptをグローバルにインストールする必要があります。

npm install typescript --save -g

tscコマンドのオプションは以下のリンクを参照してください。

https://www.typescriptlang.org/docs/handbook/compiler-options.html

実行するには以下のようにコマンドを入力します。

tsc --build
node ./dist/index.js

型宣言

TypeScript で型を指定して変数を宣言する場合は、宣言する変数の後に:numberなどを指定します。

let num: number = 123;
let str: string = "hoge";
let bool: boolean = true;

TypeScript には、型推定があるため、毎回型を指定する必要はありません。

let num = 123;

console.log(typeof num);
// > number

関数の引数の型指定

TypeScript で関数を宣言する場合、明示的に引数の型を指定(型アノテーション)する必要があります。

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

引数の型を指定する理由は、不正な入力によるエラーを防ぐためです。

以下の JavaScript コードは、2 つの引数を合計した値を返す関数を宣言しています。

// 一般的な JavaScript コード
function sum(x, y) {
  return x + y;
}

このsum関数の想定している引数の型は、明らかにnumber型です。

しかし、JavaScript の仕様では、片方にstring型を指定すると、その+演算子が文字列合成として機能します。そのため、戻り値の型が想定していた型と違っていたり、場合によってはNaN(数値ではない)が返されるなど、意図しないエラーが発生する要因となります。

sum(1, 2);
// > 3
sum(1, "hoge");
// > 1hoge

型一覧

説明
any 型が分からない場合のデフォルトの型
出来るだけ避けるべき型(最終手段)
unknown 型が分からない場合にanyの代わりに使うべき型
その型が判明するまでunknownの型の値は使用できない
boolean truefalseの 2 つの値をもつ真偽値型
let a = trueのように、一般的には型推定をさせる
number 整数や浮動小数点数、整数などの全ての数値の集まりの型
基本的には型推定させる
bigint number型では扱えないような大きな整数を扱える整数の型
numberbigintの型は違う
string 全ての文字列の集まりの型
基本的には型推定させる
symbol ユニークなキーや ID として機能する特殊な値の型
出来ることは多くない
object オブジェクトの型
オブジェクトの形状を指定する

unknown 型の例

let a: unknown = 10;

// 型が判明していないため使えない
a === "123"; // false
let b = a + 100; // Object is of type 'unknown'.

// 型が判明しているため使える
if (typeof a === "number") {
  let c = a + 100; // 110
}

object 型の例

以下のような TypeScript のオブジェクト宣言では、プロパティを認識しません。

let obj: object = {
  a: 10,
};

obj.a; // プロパティ 'a' は型 'object' に存在しません。

TypeScript でオブジェクトを宣言する場合は、以下のように記述します。

// 型推定させる
let obj1 = {
  a: 10,
};

// 明示的に指定する
let obj2: { a: string } = {
  a: "foobar",
};

class の場合

Class のインスタンスでも同様に記述します。

class Person {
  // public は this.first = first の略
  constructor(public first: string, public last: string) {}
}

let p: { first: string; last: string } = {
  first: "田中",
  last: "太郎",
};

// > { first: '田中', last: '太郎' }

プロパティを省略した場合

以下のように、空のオブジェクトやプロパティを省略した場合は、エラーが発生します。

let obj: { a: number };

obj = {};
// プロパティ 'a' は型 '{}' にありませんが、型 '{ a: number; }' では必須です。

プロパティのオプション(?, key, readonly

TypeScript では、指定されたプロパティのみが存在することを強制しており、プロパティが欠けた場合でもエラーが出ます。

しかし、省略可能なプロパティであることや予定よりも多くのプロパティが存在する場合は、別の記述で対処できます。

let obj: {
  a: number;
  b?: string;
  [key: number]: boolean;
};

obj = {
  a: 10,
  1: true,
  10: false,
};

// > { '1': true, '10': false, a: 10 }
記述 説明
a: number objは、number型のaを持つ
このプロパティがundefinedだとエラーを出す
b?: string プロパティ名の後に?を付ける
objstring型のbを持つ可能性があり、bを設定する際にundefinedでも問題ない
[key: number]: boolean objは、boolean型である複数のプロパティを任意の数だけ持つことが出来る

また、readonly修飾子で読み取り専用にすることが出来ます。

let obj: {
  readonly a: number;
} = {
  a: 10,
};

obj.a; // 10
obj.a = 1; // 読み取り専用プロパティであるため、'a' に代入することはできません。

型エイリアス(type)

TypeScript では変数と同様に、今まで指定したような型を指し示す型エイリアスを宣言することが出来ます。

typeを用いて型を宣言します。

type Age = number;

type Person = {
  name: string;
  age: Age;
};

// Person の型として宣言できる
let obj: Person = {
  name: "田中",
  age: 20,
};

合併型と交差型

例えば、以下の 2 つのクラスがあるとします。

type Cat = { name: string; purrs: boolean };
type Dog = { name: string; barks: boolean };

合併型

合併型は、2 つのクラスのどちらか、またはその両方の型を持つことを意味します。

type CatOrDogOrBoth = Cat | Dog;

// ネコの場合
let cat: CatOrDogOrBoth = {
  name: "Tama",
  purrs: true,
};

// イヌの場合
let dog: CatOrDogOrBoth = {
  name: "Pochi",
  barks: true,
};

// 両方の場合
let both: CatOrDogOrBoth = {
  name: "Tama Pochi",
  purrs: true,
  barks: false,
};

合併型はよく用いられており、例としては関数の戻り値が挙げられます。

以下の関数は、戻り値がstringnullの 2 つとなっており、表現としては合併のstring | nullとなります

function myFunc(x: boolean) {
  if (x) {
    return "OK";
  }
  return null;
}
// string | null

交差型

交差型は、2 つのクラスの両方の型を備えることを意味します。

type CatAndDog = Cat & Dog;

let both: CatAndDog = {
  name: "Tama",
  purrs: true,
  barks: false,
};

終わりに

  • 次の記事

https://zenn.dev/masa_dev/articles/a558c9ecea3e7ce09b21
GitHubで編集を提案

Discussion

ログインするとコメントできます