LoginSignup
1
0

TypeScriptの型定義がわからなくなったので整理する

Posted at

はじめに

4月から新しい現場で初めてTypeScriptを書いている。

ざっくり概要はわかっているつもりだが、「なんかよくわからないけど型のエラーが出る」ということによく遭遇するので、自分の知識の整理のためにまとめた。

前提

TypeScriptの型は便利機能のうちの一つ。

TypeScriptで書かれたファイル(.ts)は、JavaScriptのファイル(.js)にコンパイルされ、実行時には.jsファイルが実行される。
コンパイルされた.jsファイルには型の情報は出力されない。

.tsファイル(コンパイル前)
image.png

.jsファイル(コンパイル後)
image.png
→型の情報は入ってない。
JavaScriptにも型という概念は存在するが、型を宣言する構文は存在しない。

型をうまく使うことでバグを防げたり、開発を効率よくできたりする。
正しく理解することが大切。

基本的な型の定義

型定義のやりかた

TypeScriptの型を定義するには変数名:型とする。

const hoge: string = "hoge";
let piyo: number = 0;
piyo = "piyo"; // エラーになる

定義した型とは違う値を代入しようとするとエラーになる。
image.png

基本的な型として、上記のstringnumberのほか、booleannullundefinedanyがある。
JavaScriptでのデータ型とほぼ同じだが、anyはTypeScript独自の型。
どんな値でもいれることができる。

型推論

TypeScriptには型推論というものがあり、自動的に型を推測してくれる。
そのため、文字列や数値を変数にそのまま格納する分には、型宣言を書く必要がない。
基本的には型推論に任せるのがいいとされる。

型推論されている変数の例
image.png
"hoge"型になっている。
string型になっているのと同じ。
stringの中でも"hoge"という固定の文字列の型になっている。

image.png
number型になっている。

ただし、変数宣言時に初期化しない場合、型はany型になってしまう。
any型はどんな値でも受け入れてしまうので好ましくない。
image.png
→数値を入れても文字列を入れてもエラーにならない。

初期化しない場合は型を指定するべき。
image.png

いろいろな型の定義

配列

配列は型名[]で定義できる。

let arr: string[] = ["apple", "banana"];
arr.push("orange");
arr.push(100); //エラーになる

定義した型以外の値は入れられなくなる。
image.png

オブジェクト

オブジェクトは{プロパティ名:型名}で定義できる。

let person: {
  firstName: string;
  age: number;
  isValid: boolean;
} = {
  firstName: "John",
  age: 24,
  isValid: true,
};

// パラメータの不足
person = { //エラーになる
  age: 38,
  isValid: false,
};

// 存在しないパラメータ
person = {
  firstName: "Kevin",
  age: 30,
  isValid: true,
  country: "America", //エラーになる
};

// プロパティの型違い
person = {
  firstName: 0, //エラーになる
  age: 24,
  isValid: true,
};

personのあとに:(コロン)で続いているのがオブジェクトの型の定義、=(イコール)の後に続いているのが設定したいオブジェクトの値となる。

オブジェクトの型を定義することで、プロパティの過不足のチェックや、プロパティに設定されている値の型チェックを行うことができる。

定義通りのオブジェクトの形式でないとエラーになる。

プロパティの不足

image.png

存在しないプロパティ

image.png

プロパティの型違い

image.png

関数型

関数も型を設定することができる。
function 関数名(変数名:型):戻り値の型{/* 関数の処理 */ }
なお、戻り値の型にはvoidという特殊な型を設定できる。
これは戻り値を返さない場合に設定する。

function numToStr (num1:number):string {
    return num1.toString();
}

numToStr(1);

// 引数が少ない
numToStr(); //エラーになる

// 引数が多い
numToStr(1,2); //エラーになる

// 引数の型誤り
numToStr('2'); //エラーになる

// 戻り値の型誤り
function hello():string {
    return 100; //エラーになる
}

// 戻り値の型を設定しているのに返さない
function greeting():string { //エラーになる
    console.log('hello');
}

// 戻り値を返さない場合の型
function sayHi():void {
    console.log('Hi');
}

関数の型を定義することで、関数の呼び出しの際に引数の個数や型を指定することができる。
また、関数内で誤った型の値を返したり、値を返さなかったりすることを防げる。
型の定義にあった呼び出し方や、戻り値の定義をしていないとエラーになる。

引数が少ない

image.png

引数が多い

image.png

引数の型誤り

image.png

戻り値の型誤り

image.png

戻り値の型を設定しているのに返さない

image.png

アロー関数での型の定義

アロー関数も型の定義方法は同じ。
(引数:引数の型):戻り値の型が関数の型定義時の形となる。

const numToStr =  (num1:number):string => {
    return num1.toString();
}

ユニオン型

|を使うことで、複数の型を組み合わせて型を定義することができる。

let val : string | number;

val = 'hoge';
val = 100;

この場合、valstringnumberのどちらかを満たしていればよい。
関数の引数が、文字列か数値のどっちかを受け取る、みたいなときに使える。

型エイリアス

型に名前をつけてオリジナルの型を作成できる。
ユニオン型はたいてい型エイリアスを使って定義する。オブジェクトも定義できる。
type エイリアス名 = 型定義とする。
定義したエイリアスは、stringnumberなどの型と同じように、変数の型として設定できる。

type NumOrStr = string | number;
type Person = { firstName: string; age: number; isValid: boolean };

const val: NumOrStr = "hoge";

const obj: Person = {
  firstName: "John",
  age: 28,
  isValid: true,
};

NumOrStr型と、Person型を作成して、それぞれ変数の型として設定している。
エイリアスを設定することで再利用性が上がるし、適切なエイリアス名をつけることで用途が明確になる。

インターフェース

型の構造を定義することができる。
型エイリアスとにているが、インターフェースはオブジェクト型を定義するときに使う。

interface インターフェース名 {}とする。

interface Person {
  firstName: string;
  age: number;
  isValid: boolean;
}

const obj: Person = {
  firstName: "John",
  age: 28,
  isValid: true,
};

インターフェースはextendsキーワードを使うことで、他で定義した構造を引き継いで新しい構造を作ることができる。

interface Person {
  firstName: string;
  age: number;
  isValid: boolean;
}

interface BusinessPerson extends Person {
  company: string;
}

const bizPerson: BusinessPerson = {
  firstName: "Kevin",
  age: 32,
  isValid: false,
  company: "ABC",
};

PersonextendsしてBusinessPersonという新たなインターフェースを作成している。
BusinessPersonのインターフェースにはfirstNameプロパティなどはないが、bizPersonには設定できている。
これは、Personの構造を引き継いでいるためである。
ベースとなるものに新しい要素を追加することで型の定義を拡張することができる。

まとめ

基本的な宣言方法から、型の種類についてざっと確認した。
最後にジェネリクスも紹介したかったが、これはまだ自身の理解が及んでいないので、もう少し理解できてから別枠で記事を書くことにする。

型をうまく使いこなせば、間違った方法で関数を呼ぶことを防げたり、オブジェクトの中身をある程度指定できたりするのは便利だと思った。

TypeScriptを使わない理由がないのでどんどん使い倒していきたい。

1
0
0

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
1
0