5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

5分間でTypeScriptの基礎的な型を理解して使えるようになる

Last updated at Posted at 2020-08-02

今回はTypeScriptで最も重要となる「型の基礎」について説明していきたいと思います。ここでの「型」とは、データの性質のようなもので、どのように取り扱うべきかの定義だと考えてもらえばいいかと思います。

以下、TypeScriptで扱う型について記述していきます。

boolean

真偽値型ともいい、true/false の2つの値があります。条件式などでよく用いられます。

下のように宣言します。

let isChecked: boolean = true;
// - ①
let isDone: true = true;
// - ②
let isError: true = false;
// - ③コンパイルエラー

TypeScriptでは型を宣言する時に、: を用いて、その右側に型を指定します。型を指定することを**型アノテーション(type annotation)**と呼びます。

①はboolean型を明示的に伝えており、変数isCheckedにはtrue/ falseの値を入れることができます。

②には値が特定のbooleanであること、今回はtrueであることを明示的に伝えています。このように、特定のただ一つの値を表し、それ以外の値は受け入れない型のことを**リテラル型(literal type)**と呼びます。

そのため、③の例ではisErrortrueとTypeScriptに明示的に伝えたにも関わらずfalseを代入してしまっているのでコンパイルエラーが発生しています。

number

numberは整数、浮動小数点、Infinity、Nanなど、全ての数字の集合です。加減算や剰余など、数値に関する計算などをサポートします。

let num: number = 123;
// - ①
let fixedNum: 45.6 = 45.6;
// - ②

①の例は変数numがnumberであることを宣言しています。

②の例では値が特定のnumberであることを宣言しています。当然、異なる数値を代入するとコンパイルエラーが発生します。

bigint

bigintnumberで扱えなかったより大きな整数を扱うことができます。具体的には、2^53以上の数値を扱うことができます。

let bigNum: bigint = 100n;

bigintを宣言したら値の最後にnをつけます。

string

stringは文字列を表します。以下のように宣言します

let message: string = "こんにちは";
let nightMessage: "こんばんは" = "こんばんは"

symbol

symbolはES2015で導入された機能であり、オブジェクトなどで誤った値が設定されたくない時などに用いられます。シンボルはイミュータブル(不変)であり、コンストラクタを呼び出すことで宣言できます。

let key1: symbol = Symbol("hoge");
let key2: symbol = Symbol("hoge");

key1 === key2;
// false
let key3 = key1 + "fuga";
// エラー

symbolは一意なので、他のどのsymbolとも等しくなりません。

また、symbolはイミュータブルなため値を変更することもできません。

object

TypeScriptにはobjectという型を用いてオブジェクトを表現することができます。

let obj: object = {
	data: "hoge"
}

しかし、これだけではほとんど意味がなく、JavaScriptのオブジェクトであることしか宣言することができません。

オブジェクトの内部に型を指定する方法としては、以下のようにします。

let obj: {key: number} = {
	key: 1
}

こうすることで、オブジェクトのkeyというプロパティにnumberの型を宣言することができました。これをオブジェクトリテラル表記と呼びます。

また、オブジェクトリテラル表記されたオブジェクトに型の異なるプロパティを追加したり、求められているプロパティを省略した例を見てみましょう。

let onamae: {
  firstName: string
  lastName: string
} = {
  firstName: "Yoshio"
};
// エラー

onamae = {
	nickName: "Yoshi"
};
// エラー

このように、予めオブジェクトに期待しているプロパティがなかったり、期待していないプロパティが代入されてしまうとTypeScriptはエラーを出します。

しかし、時にはプロパティを省略したり、追加したくなると思います。そんな時には以下のように記述します。

let address: {
  country?: string,
  [code: number]: string
};

address = {
  1: "one"
};
// - ①

address = {
  country: "America",
  2: "two",
  3: "third"
}
// - ②

address = {
  country: "Japan"
}
// - ③

見慣れない表記が出てきたと思います。addressオブジェクトのcountryプロパティの右側に? がついていますが、これは省略可能であることを示しています。具体的にはundifinedを受け入れることを表し、この場合はcountryプロパティはstringundifined両方の型を受け入れます。

そのため、①の例のようにcountryプロパティが省略されてもエラーは出ません。

また、codeプロパティは[] に囲まれていますが、この構文はインデックスシグネチャといい、オブジェクトが多くのプロパティを含む可能性を示します。インデックスシグネチャを使うことで、明示的に宣言したプロパティ以外にも多くのキーを安全にオブジェクトに追加することができます。

②のように、複数のnumberの型を持つプロパティとstringの型を持つ値を与えることができます。

さらに、③の例のように省略することも可能です。

ただし、インデックスシグネチャには注意点があり、プロパティの型はnumberstringしか使えないということです。

配列

TypeScriptで配列に型をつける場合は以下のよう型アノテーションの後に[]をつけます。

let stringArray: string[];
stringArray = ["hoge", "fuga"];

タプル

タプルは配列の派生であり、配列の長さを固定することができます。以下に例を示します。

let numStringTuple: [number, number, string] = [1, 2, "3"];

let optionalTuple: [number, number?] = [1];

let variableTuple: [string, ...string[]] = ["A", "B", "C", "D"];

タプルは配列宣言と似ており、違いは[] 内に型を記述することです。タプル内で? 表記を使うことで省略可能な要素を記述することもできますし、最後の例のように可変長の要素も使えます。

any

anyはなんでも代入可能な型です。最終手段の型であり、TypeScriptの恩恵を全く受けれなくなる(型安全性を保証できなくなる)ので、極力使わないほうがいいでしょう。

let allOK: any;
allOK = "ok?"
allOK = 123;
allOK = true;
// 全て許容される

unknown

unknownanyと同様、型がわからない時に使う場合がありますが、こちらを優先して使ってください。なぜならunknownは与えられた値の型を絞り込むまで値を使用させないようにしてくれるからです。例を見てみましょう。

let unknownValue: unknown = 100;
let u = unknownValue + 1;
// コンパイルエラー: Object is of type 'unknown'

if (typeof unknownValue === "number") {
  let num = unknownValue + 100;
}
// unknownValueがnumberであることが保証されたため使用することができる!

変数unknownValue はunknown 型を与えらたので、その型がわからないまま使用する(この例では1を足す)と、エラーとなってしまいます。

しかし、型をnumberに絞り込んであげることで型が保証され、その値を使用することができることができます。これらからanyよりも型安全性が高く、こちらを使うことを推奨します。

null, undefined

JavaScriptには値がないことをnull, undefinedを用いて表します。nullは値が入っていないことを意味し、undefinedはまだ定義がされていないことを意味します。TypeScriptではこれらを型として扱うことができます。

let n: null;
let u: undefined;

void, never

さらにTypeScriptには値がないことを識別するためにvoid , never の2つの型を用意しています。voidは明示的に何にも返さない関数の戻り値の型であり、neverは決して返ることのない(例外をスローする場合や、無限ループなど)関数の型です。

function logger() : void {
  console.log("何も返さないよ");
}

function errorGenerator() :never{
  throw TypeError("決して返らないよ");
}

型エイリアス

変数に名前をつけて、値を代入できるのと同様に、TypeScriptでは型に名前をつけて宣言することができます。これを型エイリアス(type alias)と呼びます。宣言方法は、type 型名とします。

以下に例を示します。

type Coffee = {
  name: string,
  country: string,
  code: number,
};

新たに定義した、Coffee を利用するには、以下のようにします。

// アラビカコーヒーを定義
const arabica: Coffee = {
  name: "Arabica",
  country: "Ethiopia",
  code: 1,
}

// Coffeeで定義されていたcodeプロパティが存在しないためエラー
const american: Coffee = {
  name: "American coffee",
  country: "America"
};

新たにコーヒーの種類として、型エイリアスを利用してアラビカコーヒーを定義しています。下の例ではCoffeeの型エイリアスで指定していたcodeプロパティが欠如しているためエラーとなります。

また、JavaScriptの変数と同様に、基本同じ型を2度宣言することはできません。さらに、型エイリアスはブロックスコープなため、スコープの有効範囲はブロック内に限ります。そのため、以下のように別のブロックでは同じ型を宣言することができます。

type status = "done";
type status = "progress";
// コンパイルエラー : 2度同じ名前の型エイリアスを宣言できない

if (isStatus) {
  type status = "done";
} else {
  type status = "progress";
}
// 型エイリアスはブロックスコープなので2度同じ名前の型エイリアスを宣言したことにならない

合併型(Union Types)と交差型(Intersection Types)

これまでたくさんの型について紹介してきましたが、これらの型を組み合わせたりしてもっと複雑な型を表現したい場面があると思います。そんな時に便利なのが**合併型(Union Types)交差型(Intersection types)**です。

合併型は型A, Bの和集合を表す型で、交差型は積集合を表す型です。合併型は|、交差型は&演算子を使って表現します。具体例を見ていきましょう。

type Kick = {
  effect: boolean,
  hit: boolean,
};

type Punch = {
  hit: boolean,
  damage: number,
};

type KickOrPunch = Kick | Punch;
type KickAndPunch = Kick & Punch;

型エイリアスKickOrPunchは型KickとPunchの合併型であり、KickAndPunchは交差型です。ではそれぞれはどのような型を受け入れるのでしょうか。

// 合併型なので、Kick, Punch型両方のプロパティが使える
const SuperAtack: KickOrPunch = {
  effect: true,
  hit: true,
  damage: 10,
};

// 合併型なため、どちらかの型ということを表せれば良い
const normalAtack: KickOrPunch = {
  hit: true,
  damage: 3,
};

// 交差型なので、合併型同様Kick, Punch型両方のプロパティが使える
const doubleAtack: KickAndPunch = {
  effect: true,
  hit: true,
  damage: 10,
};

// 交差型は集合元の型全てのプロパティを使わなくてはならない
const errorNormalAtack: KickAndPunch = {
  hit: true,
  damage: 3,
};
// エラー: Kick typeのeffectプロパティがないため

合併型であるSurperAtacknormalAtackはKick、Punchどちらかの型か両方の型であることを示せればよいので、両方の型か一方の型のプロパティを全て使うことができます。

一方交差型は集合元のプロパティを全て宣言する必要があります。

また、合併型は関数の引数などで、以下のような使い方をよくします。

function atack(power: string | number) {
	...
}

これは関数の引数powerstringnumberどちらかであることを示しています。

このようにTypeScriptでは他の静的型付け言語で扱えるような型やそれ以上にさまざまな型を宣言できることがわかっていただけたと思います。

もっと詳しく学びたい方は、是非ブログも見ていってください!

それでは🙌

参考: https://www.typescriptlang.org/docs/handbook/basic-types.html

5
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?