概要
Typescript
の初学者である私が、Typescript
を使用した開発を行う為、自分なりに学んだ事や分かった事をまとめます。今回の記事は、私のメモにみたいな物のになりますので、有益な情報は存在しないかと思われます。予めご了承頂ければと思います。
静的型付けと動的型付の違い
動的型付けの例
-
JavaScript
では、動的型付け言語に分類される - 変数の宣言時にデータ型を指定しなくてもよい
- 実行時にその変数がどのようなデータ型を持つかが動的に決まる
let myVar = "Hello World!";
myVar = 123; // OK, myVar is now a number
myVar = true; // OK, myVar is now a boolean
静的型付けの例
-
Typescript
では、静的型付け言語に分類される - プログラムの実行前に型のチェックを行う
- 型が不一致の場合は、コンパイルエラーとして指摘する
// データの受け渡しをする際、型が同じであれば正常にコンパイル可能となる
function add(a: number, b: number): number {
return a + b;
}
let result = add(1, 2);
console.log(result);
// エラーの例
function add(a: number, b: number): number {
return a + b;
}
// functionの呼び出しとfunction側の型が同じでない為、コンパイルエラーが発生
let result = add("1", "2"); // コンパイルエラー
console.log(result);
TypeScriptで使用されるデータ型
- TypeScriptの場合、列挙型などの型を表す際は、先頭の文字を大文字にする事が推奨されてる
データ型 | 説明 |
---|---|
number | 数値を表す 整数または浮動小数点数を表す事もできる |
string | 文字列を表す 文字列はクォーテーションで囲む |
boolean | 真偽値を表す trueまたはfalseのいずれかの値を返す |
any | どの型の値でも代入可能 |
void | 値が存在しないことを表す 主に関数の戻り値の型として使用する |
null | 値がnullであることを表す |
undefined | 値が未定義であることを表す |
object | オブジェクトを表す |
Array | 配列を表す |
Tuple | 固定数の要素を持つ配列を表す |
Enum | 列挙型を表す |
Union | 複数の型の表現が可能 |
Intersection | 複数の型の共通部分を表現可能 |
Function | 関数型を表す |
関数(function)
関数宣言の基本的な書き方
- 一連の処理を行うコードブロックで、
JavaScript
/TypeScript
ではfunction
を使って定義する
// 構文
function 関数名(引数1: 型1, 引数2: 型2, ...): 戻り値の型 {
// 関数の処理
return 戻り値;
}
// 例文
function addNumbers(num1: number, num2: number): number {
return num1 + num2;
}
アロー関数
- 関数を省略して書く方法
// 構文
// returnがない場合は、返り値の型の指定は省略可能
const 関数名 = (引数1: 型, 引数2: 型, ...) : 返り値の型 => {
// 関数の処理
return 戻り値;
}
// 例
const add = (num1: number, num2: number) : number => {
return num1 + num2;
}
// 引数が1つの場合の構文
// returnがない場合は、返り値の型の指定は省略可能
const 関数名 = (引数: 型) : 返り値の型 => {
// 関数の処理
return 戻り値;
}
// 例
const double = (num: number): number => {
return num * 2;
}
オプション引数(引数を省略する書き方)
- 関数に引数を渡す際に、その引数を省略できる様にする為の機能
// オプションの引数を使用した構文
// 引数の後ろに`?`を指定する
function 関数名(必須引数: 型, オプション引数?: 型) {
// 関数の中身
}
// 例
function greet(name: string, greeting?: string) {
if (greeting) {
console.log(`${greeting}, ${name}!`);
} else {
console.log(`Hello, ${name}!`);
}
}
greet("Alice"); // Hello, Alice!
greet("Bob", "Good morning"); // Good morning, Bob!
オプション引数(デフォルト値を設定する書き方)
- 引数を省略してもデフォルト値が自動的に設定される機能
// オプションの引数にはデフォルト値を設定した構文
// デフォルト値は、型のうしろに`= 〇〇`と記述する
function 関数名(必須引数: 型, オプション引数: 型 = デフォルト値) {
// 関数の中身
}
// 例
function greet(name: string, greeting?: string = 'Hello'): string {
return `${greeting}, ${name}!`;
}
console.log(greet('John')); // Hello, John!
console.log(greet('Sarah', 'Hi')); // Hi, Sarah!
Class(クラス)
クラスの定義
- オブジェクトを作成する為の設計や仕様を定義したもの
// 構文
class クラス名 {
}
コンストラクタ
- クラス内で使用できるメソッド
- クラスをインスタンス化した時に、最初に自動的に呼び出される特殊なメソッドとなる
constructor(引数1: データ型1, 引数2: データ型2, ...) {
// コンストラクターの処理
this.プロパティ名 = 初期値;
}
インスタンス化
- インスタンス化とは、クラスの設計図を元に具体的なオブジェクトを作り出す事を言う
- インスタンス化する事で、クラス内に定義した関数やプロパティーを呼び出して、オブジェクトとして使用できる
class クラス名 {
// プロパティやメソッドを定義する
}
// インスタンス化する場合は、new クラス名()で行う
const 任意の変数名 = new クラス名();
動作方法
- クラスを定義した後、クラス内にプロパティーとコンストラクタを配置
- インスタンス化をして、オブジェクトを作り出す流れを作成した
// 構文を使用した構築例
class クラス名 {
プロパティ1: データ型;
プロパティ2: データ型;
constructor(引数1: データ型1, 引数2: データ型2) {
this.プロパティ1 = 引数1;
this.プロパティ2 = 引数2;
}
メソッド1() {
// メソッドの処理
}
メソッド2() {
// メソッドの処理
}
}
// クラスのインスタンス化
const 変数名 = new クラス名(引数1: データ型1, 引数2: データ型2);
// オブジェクトを作成
変数名.メソッド1();
変数名.メソッド2();
// 例
class Animal {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayName() {
console.log(`私の名前は${this.name}です`);
}
sayAge() {
console.log(`私の年齢は${this.age}歳です`);
}
}
let cat = new Animal("にゃんこ", 3);
cat.sayName(); // 私の名前はにゃんこです
cat.sayAge(); // 私の年齢は3歳です
private修飾子
- クラス内でのみアクセス可能なプロパティまたはメソッドを宣言する為に使用
-
private
で設定されているプロパティまたはメソッドは、クラス外からインスタンス化できない
// 構文
class クラス名 {
// プライベートプロパティー
private 変数名: データ型;
// コンストラクターでプライベートプロパティーに値をセット
constructor(private プライベート変数名: データ型) {
this.変数名 = プライベート変数名;
}
// プライベートメソッド
private プライベートメソッド名() {
// メソッドの処理
}
// パブリックメソッド
public パブリックメソッド名() {
// パブリックメソッドからプライベートメソッドを呼び出す事ができる
this.プライベートメソッド名();
}
}
// クラスのインスタンス化
const 変数名 = new クラス名(プライベート変数の初期値);
// プライベートメソッドにアクセスしている為、エラーとなる
変数名.プライベートメソッド名();
// パブリックメソッドからプライベートメソッドを呼び出すことができる
変数名.パブリックメソッド名();
// 例
class User {
private name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
private isAdult() {
return this.age >= 20;
}
public introduce() {
console.log(`私の名前は${this.name}です。`);
if (this.isAdult()) {
console.log(`成人です。`);
} else {
console.log(`未成年です。`);
}
}
}
const user1 = new User('太郎', 25);
user1.introduce(); // 私の名前は太郎です。成人です。
// 下記の呼び出しはprivateメソッドにアクセスしているためエラーとなる
user1.isAdult();
setter
- クラスのプロパティに値を設定する為の特殊なメソッド
- クラス内部でのデータの整合性を保ちつつ、外部からプロパティに値を設定する事が可能
-
setter
を使用する場合、プロパティ名の前にアンダースコアを付ける事が一般的とされている
// 構文
// `set`というキーワードを使用して定義する
set プロパティ名(引数: 型) {
// プロパティに値を設定する処理
}
// 例
class Person {
// 一度this._ageに値が入るとその値をずっと保持し続ける
// (今回の場合は、25が格納された状態なる。0以上の数値が設定されない限り、永遠に25の値を保持する)
private _age: number;
// 今回の処理では、valueに25の数値が格納さている
set age(value: number) {
if (value < 0) {
console.log("年齢は0以上で設定してください。");
return;
}
// valueが0以上の場合、this._ageには、その数値が入る
// (今回の処理では25がthis._ageに格納される)
this._age = value;
}
}
// クラスのインスタンス化
const person = new Person();
// ageプロパティに値が設定される
person.age = 25;
console.log(person.age); // 出力結果: 25
person.age = -5; // "年齢は0以上で設定してください。"と出力される
getter
- プロパティの値を読み取る為に使用する
-
getter
を使用する場合、プロパティ名の前にアンダースコアを付ける事が一般的とされている
// `get`というキーワードを使用して定義する
get プロパティ名(): 型 {
// プロパティから値を取得する処理
}
// 例
class Person {
private _age: number;
constructor(age: number) {
this._age = age;
}
get age() {
return this._age;
}
}
// クラスのインスタンス化
const person = new Person(25);
console.log(person.age); // 出力結果: 25
クラスの継承
- 既存のクラスを基にして、新しいクラスを定義する
-
protected
は、クラス内部とそのサブクラス内でのみアクセス可能なプロパティやメソッドを定義する際に使用する -
protected
を使用する場合、プロパティ名の前にアンダースコアを付ける事が一般的とされている
// 構文
class クラス名 extends 親クラス名 {
protected _変数名: 型;
// サブクラスのプロパティやメソッドを定義
}
// 例
class Animal {
protected _name: string;
// "Tama"の値が`this._name`にセットされる
constructor(name: string) {
this._name = name;
}
eat() {
console.log(`${this._name} is eating`);
}
}
class Cat extends Animal {
meow() {
console.log(`${this._name} is meowing`);
}
}
// クラスのインスタンス化
const myCat = new Cat("Tama");
myCat.eat(); // "Tama is eating"と出力される
myCat.meow(); // "Tama is meowing"と出力される
メンバ
- クラス内に定義されたもの(
フィールド
、メソッド
、アクセサ
、インデックスシグネチャ
等)は全てクラスのメンバと扱われる - メンバは、インスタンス化されたオブジェクトに紐づいている為、オブジェクトを生成しないとアクセスできない
// 下記に記載したものは、全てメンバの括りとなる
class Example {
// プロパティ(フィールド)
private _name: string;
// メソッド
sayHello() {
console.log(`Hello, ${this._name}!`);
}
// インデックスシグネチャ
[key: string]: string;
// コンストラクター
constructor(name: string) {
this._name = name;
}
// アクセサ(getter/setter)
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
}
静的メンバ
- インスタンス化することなく直接アクセスする事が可能
- 静的メンバは、
static
を用いて定義する
class クラス名 {
static 静的メンバ名: データ型 = 初期値;
}
// 例
class MyClass {
static myStaticProperty: string = "Hello";
static myStaticMethod() {
console.log(MyClass.myStaticProperty);
}
}
// アクセス可能
// staticがついている為、インスタンス化しなくても呼び出しできる
console.log(MyClass.myStaticProperty); // "Hello"
MyClass.myStaticMethod(); // "Hello" をコンソールに出力
// アクセス不可能
const myInstance = new MyClass();
console.log(myInstance.myStaticProperty); // エラー
myInstance.myStaticMethod(); // エラー
インターフェース
インターフェースの基本的な書き方
- オブジェクトの型に、名前を定義する為の仕組み
- インスタンスの生成はできない
// 構文
// インターフェイスを定義
interface インターフェース名 {
プロパティ名: 任意の型を指定;
// ...
}
// インターフェースで定義されたプロパティに具体的な値を付与
const 変数名: インターフェース名 = {
プロパティ名: 値を指定,
// ...
};
// 呼び出し方法
const 任意の変数名 = 変数名.プロパティ名;
// 例
interface Person {
name: string;
age: number;
}
const person: Person = {
name: "John",
age: 30,
};
console.log(person.name); // "John"を出力
インターフェースの継承
- 既存のインターフェースを継承して新しいインターフェースを定義する事が可能
- 継承したインターフェースのプロパティとメソッドは、新しいインターフェースに含める事が可能
interface 親インターフェース名 {
// 親インターフェースのプロパティやメソッドを定義
}
interface 子インターフェース名 extends 親インターフェース名 {
// 子インターフェースのプロパティやメソッドを定義
}
// 例
interface Person {
name: string;
age: number;
}
interface Employee extends Person {
company: string;
position: string;
}
// 親と子のプロパティーを設定する事が可能
const employee: Employee = {
name: "John",
age: 30,
company: "ABC Corp",
position: "Manager"
};
オプションのプロパティ
- インターフェースで定義したオブジェクトのプロパティの内、必須でないプロパティーを取り扱う際に使用する
構文
interface インターフェース名 {
// オプションのプロパティは、プロパティ名の後に`?`を付けて定義する
プロパティ名?: プロパティの型;
// ...
}
// 例
interface Person {
name: string;
age?: number;
}
// ageのプロパティーを指定しなくても正常に処理が動く
const person1: Person = { name: "Alice" };
// 当然、ageのプロパティーを指定しても処理は動作する
const person2: Person = { name: "Bob", age: 20 };
クラスとインターフェースを組み合わせ
implements
- インターフェースとクラスを組み合わせて実装する際に使用する
// 構文
class クラス名 implements インターフェース名 {
// インターフェースで定義されたメソッドやプロパティを実装
}
// 例
// インターフェイスを定義
interface Animal {
name: string;
age: number;
move(): void; // 戻り値はない(返却する値がない)
}
// クラスを定義
class Cat implements Animal {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
move() {
console.log(`${this.name}が歩きました`);
}
}
// クラスのインスタンス化(オブジェクトの生成)
// constructor の name に"Tama"の値を、 age に3の値を渡している
const cat = new Cat("Tama", 3);
// `cat`オブジェクトが生成されている為、cat.メンバで呼び出しが可能
cat.move(); // "Tamaが歩きました"と出力される
ジェネリクス
- クラスや関数、インターフェースの型を抽象化する機能
ジェネリクスを使用した関数
// 構文
// 型パラメータ(Tと記載されている部分)には、Typeの頭文字である`T`を使用する事が推奨されている
function 関数名<T>(引数名: T): T {
// 関数の処理
return 引数名;
}
// 例
function identity<T>(arg: T): T {
return arg;
}
const str = identity('hello'); // strの型はstringになる
const num = identity(123); // numの型はnumberになる
ジェネリクスを使用したクラス
// 構文
class クラス名<型パラメータ> {
プロパティ: 型パラメータ;
constructor(引数: 型パラメータ) {
this.プロパティ = 引数;
}
メソッド(引数: 型パラメータ): 型パラメータ {
// メソッドの処理
return 引数;
}
}
// 例
class Box<T> {
private item: T;
constructor(item: T) {
this.item = item;
}
getItem(): T {
return this.item;
}
setItem(item: T): void {
this.item = item;
}
}
// クラスのインスタンス化
// 引数に型と値を渡している(コンストラクトの引数に値が送られる)
const box1 = new Box<string>("Hello");
console.log(box1.getItem()); // 出力結果: "Hello"
// box1のオブジェクトが生成されている為、引数に"world"と言う値を送る
// 上記の工程を行う事で、 privateで設定したitemにworldの値がセットされる
box1.setItem("world");
console.log(box1.getItem()); // 出力結果: "world"
// 引数に型と値を渡している(コンストラクトの引数に値が送られる)
const box2 = new Box<number>(123);
console.log(box2.getItem()); // 出力結果: 123
box2.setItem(456);
console.log(box2.getItem()); // 出力結果: 456
ジェネリクスに制約を与える
// 構文
function 関数名<T extends 制約条件>(引数: T): 戻り値の型 {
// 関数の処理
}
// 例
// 引数の型が、string または numberのみvalueに登録できる
class Box<T extends string | number> {
value: T;
constructor(value: T) {
this.value = value;
}
add(x: T, y: T): T {
return x + y;
}
}
const box1 = new Box<string>("Hello");
const box2 = new Box<number>(2);
console.log(box1.add(box1.value, box2.value));
// 出力結果: "Hello2"
外部モジュール
- 外部モジュールとは、別のファイルに分割されたコードの事を示す
構文を下記に示す
任意のファイル名1.ts
export function 関数名(引数: 型): 戻り値の型 {
// 関数の処理
}
export const 変数名: 型 = 任意の値;
任意のファイル名2.ts
import { プロパティ名またはメソッド名 } from './ファイル名';
プロパティ名またはメソッド名;
サンプル例を下記に示す
sample-module.ts
// 外部モジュールで公開する変数や関数をexportで指定
export function sayHello() {
console.log(`Hello, world!`);
}
export const name = "John";
index.ts
// importで外部モジュールを読み込み、利用する
import { name, sayHello } from './sample-module';
console.log(name); // "John"
sayHello(); // "Hello, world!"
参考資料
感想
今回Typescript
の基本文法を学んだ事で、Typescript
について理解できたと感じております。また、今回の勉強を通して、改めオブジェクト指向についても理解する事ができたので、良かったと感じております。あとは実践にて、公式ドキュメントを熟読しながら開発ができるスキルを身に付けれればと考えています。