【TypeScript】データ型の定義・活用方法をまとめてみました

# はじめに
TypeScriptを使ったReactのアプリを作るため、**TypeScriptの基礎**を学習しました。

データ型のさまざまな定義方法を学んだので、整理しつつアウトプットしていきます。


#TypeScriptのデータ型
```tsx
//型推論
let greet = "hello"; //文字列リテラル
let number = 2; //number型
let bool = true; //boolean型

//アノテーション
let bool: boolean = true; //bool: booleanのように”明示的”に型を記述することも可能

//配列の型定義
let array1 = [true, false]; //boolean[]
let array2 = [0,1, "hello"]; //[number | string]

//オブジェクトの型定義
interface NAME {
  firtst: string;
  last: string | null; //このようにnullをデータ型として渡すことも出来る　　　　　　　　　　　
}
// last?: string とすれば、下記nameObjで{first: "Taro"}飲みの記述でもエラーにならない

// オブジェクトの型を変数に定義する
let nameObj: NAME = {first: "Taro", last: "Yamada"}; //


// データ型を定義した関数
const func1 = (x: number, y: number) => {
  return x + y;
};
```
`let number = 2`のように、変数や定数を定義するとTypeScriptが自動で**”型推論”**といものを行ってくれます。その結果、**エディタ上で変数にホバー**すると`number: number`のように、データ型を確認することができます。

`interface NAME = {name: string}`のようにオブジェクトでデータ型を定義することも可能で、さらにそれを別の変数などに当てはめることもできます。

例えば、`let nameObj: NAME = {name: "hoge"}`のような形です。


#Intersection Types
```tsx
type PROFILE = { //type1の型を定義
  age: number;
  city: string;
};

type LOGIN = { //type2の型を定義
  username: string;
  password: string;
};

type USER = PROFILE & LOGIN; //type1と2をUSERというtypeに代入

const user: USER = { //userという定数にtype USERというオブジェクトのデータ型を定義して、それぞれのデータ型に実際のデータを定義する
  age: 30,
  city: "Tokyo",
  username: "xxx",
  password: "yyy",
}
```

`type PROFILE ~` `type LOGIN ~` のように、それぞれ別に作成した”オブジェクトのデータ型同士”を、一つに接続することも出来ます。
その際は`type USER = PROFILE & LOGIN`と記述します。+ではなく**"＆(アンパサンド)"**を使用します。

一つにしたtypeを定数などに定義して使う場合は`const user: USER = {}`として、{}の中に、それぞれのtypeで使われていた属性と、そのデータ型の一致するstringやnumberなどを入力します。


#UnionTypes
```tsx
// |(バーティカルバー)でつなぐUnion Types
let value: boolean | number; //変数へのデータ型定義。|でつなぐことで複数のデータ型を定義できる
value = true; 
value = "hello"; //stringは定義していないのでエラーになる
value = 3;

// 配列への複数データ型定義
let arrayUni: (number | string)[]; //変数arrayUniという配列にデータ型を定義
arrayUni = [0, 1, 2, "hello", true] //booleanは定義していないのでエラーになる;
```

`let value`というように、何かしら変数名を定義するとして、その変数に"Union Types"でデータ型を複数定義できます。
その際は **” | (バーティカルバー)”** を用いて`let value: boolean | number`のように記述します。

配列にも同じようにUnion Typesを使用できて、`let arrayUni: (number | boolean)[];`
とすることで`arrayUni = [1, 2, 3, true];`のように値を代入できます。


#Literal Types
```tsx
// Literal TypesとUnion Typesの融合
let company : "Google" | "Facebook" | "Amazon";
company = "Facebook";
company = "Apple"; // Appleはリテラルとして定義されていないのでエラー

let memory: 256 | 512;
memory = 256;
memory = 555; // リテラルが定義されていないのでエラー
```

具体的な**”文字列リテラル”**も、あらかじめデータ型として変数や定数に定義できます。
" | "を使用してUnion Typesとして複数定義も可能です。


#typeof
```tsx
let msg: string = "Hi"; //string型をmsgに定義
let msg2: typeof msg; //msg2にmsgのデータ型を代入
msg2 = "hello"; //msg2はmsgを継承しているのでstringの”hello”を定義できる

// オブジェクトで定義したデータ型を継承
let animal = {cat: "small cat"}; // {cat: string}として定義
let newAnimal: typeof animal = {cat: "big cat"}; // typeofでstring型を継承したので"big cat"を定義できる

```
`typeof` を使うことで、データ型を定義した変数を、別の変数に定義できます。

上記コードの例でいくと、`msg2`は`msg`で定義されているstring型を継承しているので`msg2 = "hello"` というstring型の値を代入することが出来るようになります。

また、オブジェクトで定義したデータ型も同じ用に継承できます。
`let animal = {cat: "small cat"};`は型推論で`{cat: string}`となっているので、
`let newAnimal: typeof animal = {cat: "big cat"};` のように別の変数に、strin型の"big cat"を代入できます。

#keyof
```tsx
type KEYS = {
  primary: string;
  secondary: string;
}

let key: keyof KEYS; //KEYSの属性(primary, secondary)を継承
key = "primary"; //keyである"primary"もしくは"secondary"という「string」しか受け付けない

// keyofとtypeofを組み合わせる
const SPORTS ={
  soccer: "Soccer", // key = soccer, type = stringという状態
  baseball: "Baseball",
}

let keySports: keyof typeof SPORTS; // SPORTSのkeyとtypeを継承する
keySports = "soccer"; 
keySports = "baseball"; //baseballというkeyと、Baseball(string)それぞれの要素を持った"baseball"という文字列を定義できる
```
`let key: kyeof KEYS` は”KEYSの中の、string型の各keyを変数keyに渡す”ということなので、変数keyには`key = "primary"` もしくは `key = "secondary"` が代入できるということになります。



#enum(列挙型)
```tsx
enum OS {
  Windows, // 0
  Mac, // 1
  Linux, // 2 ※enumで定義することで、それぞれに自動的に番号が割り当てられる
}

interface PC {
  id: number;
  OSType: OS; // enumで定義したデータを継承
}

const PC1: PC = {
  id: 1,
  OSType: OS.Windows, // OS.まで打つと、エディタではWindows, Mac, Linuxと予測が表示される。enumで定義してこのように使うほうがメンテナンス性の向上や、バグが起こりにくくなるというメリットがある。
}

const PC2: PC = {
  id: 2,
  OSType: OS.Mac,
}
```
`const PC1: PC = {...省略}`で`interface`で定義したデータ型(enumも含む)を継承できる。
**”{}(波括弧)”**の中で`OSType: OS.Windows`とすることで、このOSTyep:には"0"番が割り当てられていることになる。


#型の互換性
```tsx
const comp1 = "test";
let comp2: string = comp1; //”抽象的”なstring型のcomp2に”具体的”な文字列リテラルを持つcomp1は代入可

let comp3: string = "test" 
let comp4: "test" = comp3; //comp4: "test"は文字列リテラルで具体的なので、そこに抽象的な"string"型を持つcomp3は代入できない

let funcComp1 = {x: number} => {};
let funcComp2 = {x: string} => {};

funcComp1 = funcComp2; //xのデータ型が違うのでエラー
```
”抽象” → ”具体”は代入できるが、”具体” → ”抽象”は代入できない。

#Generics(ジェネリックス)
```tsx
interface GEN<T> { 
  item: T;
}
const gen0: GEN<string> = {item: "hello"};
const gen1: GEN = {item: "hello"}; // GENのあとに<>を付けないとエラー
const gen2: GEN<number> = {item: 12};

interface GEN1<T = stirng> { // あらかじめTにstring型を定義
  item: T;
}
const gen3: GEN1 = {item: "hello"}; //GEN1にはstring型が定義されているので、<>は付けなくても{item: "hello"}を定義できる

interface GEN2<T extends string | number> = {
 item: T;
}
const gen4: GEN2<boolean> = {item: ture}; //　エラー。extendsでbooleanは定義していない

function funcGen<T>(props: T) {
  return {item: props};
}
const gen5 = funcGen("test") //明示的にfuncGen<string>("test")と書くことも可
const gen6 = funcGen<string | null>(null) // union typesでデータ型を指定することもできる

function funcGen1<T extneds string | null>(props: T) { //あらかじめデータ型を定義
  retunr {value: props};
}
const gen7 = funcGen1("hello");
const gen8 = funcGen1(123); //extendsでnumberは定義されていないのでエラー

interface Props {
  price: number;
}

function funcGen3<T extends Props>(props: T) { //prosに引数として受け取れるデータは{price: number}
  return {value: props.price};
}
const gen9 = funcGen({price: 10});

// funcGen3をアロー関数で記述したら
const funcGen3 = <T extends Props>(props: T) => {
  return {value: props.price};
}
```
GEN<T>の”T”は**「エイリアス」**と呼ばれ、その他には大文字の”U”もよく使われる。

また、ジェネリックスはReactでいうところの”props”のように<T>として記述している箇所に、データ型や値を代入できる。


#JSON型推論
`jsonplaceholder`でWeb検索して、そのページ内の`/users`というリンクから下記データをコピー。
`data.json`というファイルを作り、そこにペーストして使っていくという設定。

```data.json
[
  {
    id: 1,
    name: "Leanne Graham",
    username: "Bret",
    email: "Sincere@april.biz",
    address: {
        street: "Kulas Light",
        suite: "Apt. 556",
        city: "Gwenborough",
        zipcode: "92998-3874",
        geo: {
          lat: "-37.3159",
          lng: "81.1496"
        }
    },
    phone: "1-770-736-8031 x56442",
    website: "hildegard.org",
    company: {
      name: "Romaguera-Crona",
      catchPhrase: "Multi-layered client-server neural-net",
      bs: "harness real-time e-markets"
    }
  },
  {
    id: 2,
    name: "Ervin Howell",

    // 以下省略 //
```

```App.tsx
import Data from './data.json';

type USERS = typeof Data; //data.jsonで型定義されているデータをUSERSの中に代入
```
これだけの量のデータ型を１から定義するのは大変。
JSON型推論からデータ型を取得することで、作業量を大幅に削減することができる。


#React Hooks Props型
```App.tsx
const App: React.FC = () => { //Reactのfunctional componentに型定義するときの記述
  return (
    <div>
    
    </div>
  )
}
```

```TestComponent.tsx
import React from 'react';

interface Props {
  text: string;
}

const TestComponent: React.FC<Props> = (props) => { //React.FC<Props>とすることで引数のpropsの型を、interfaceで定義したPropsの型にすることができる
  return (
    <div>
      <h1>{props.text}</h1>
    </div>
  )
}

export default TestComponent;

```

```App.tsx
import TestComponent from './TestComponent';

const App: React.FC = () => { //Reactのfunctional componentに型定義するときの記述
  return (
    <div>
      <TestComponent text="hello world" /> //propsにtext="hello world"を渡す。渡す属性はtextで、値はstring型である必要がある。
    </div>
  )
}
```

functional componentに`React.FC<>`と付け足すことで、TypeScriptのデータ型をpropsに渡す事ができる。
TestComponentのpropsには"text: string"がデータ型として定義されているので、App.tsxでTextComponentを使用する際は、そのpropsのtextに`”hello world”`などの"string型"の値を代入する。それ以外のデータ型ではエラーになる。


#React Hooks useState
```TestComponent.tsx
import React, {useState} from 'react'; //useStateをインポート

interface Props {
  text: string;
}

interface UserData {
  id: number;
  name: string;
}

const TestComponent: React.FC<Props> = (props) => { 
  const [count, setCount] = useState<number | null>(null); //ジェネリックスを使ってunion typesで複数の型定義も出来る
  const [user, setUser] = useState<UserData>({id: 1, name: "dummy"}); //UserDataで定義したデータ型しか受け付けない

  return (
    <div>
      <h1>{props.text}</h1>    
      <p>{count}</p> //nullが返る
    </div>
  )
}

export default TestComponent;

```
useState()にもジェネリックスを使って`useState<number>(3)`のようにデータ型を渡して、それに適合する値をstateの初期値として代入することができる。


#Event handler: データ型
```TextComponent.tsx
import React, {useState} from 'react'; 

interface Props {
  text: string;
}

interface UserData {
  id: number;
  name: string;
}

const TestComponent: React.FC<Props> = (props) => { 
  const [count, setCount] = useState<number | null>(null); 
  const [user, setUser] = useState<UserData>({id: 1, name: "dummy"}); 
  const [inputData, setInputData] = useState("");

  const handleInputChange = (e: React.changeEvent<HTMLElement>) => // e: のデータ型はonChange={handleInputChange}を”ホバー”した時に現れるデータ型をコピーする
    setInputData(e.target.value); //inputタグに入力された値を取得してinputDataに代入
  return (
    <div>
      <h1>{props.text}</h1>
      <p>{count}</p>
      <input type="text" value={inputData} onChange={handleInputChange} />
      <h1>{inputData}</h1> //handleInputChange関数でinputDataにデータを代入して、それをリアルタイムでここに表示する
    </div>
  )
}

export default TestComponent;
```
`e: `のデータ型は`onChange={}`をホバーした時に現れるポップ？に表示されるので、それをコピペする。


#まとめ
下記、”参考”欄に掲載しているudemyの講座から、コードなどを抜粋してまとめました。

大まかに概念や使い方は把握できたので、あとは実際の開発の中で使いながら、自分の”血肉”としていきたいです。

しっかりとデータ型を定義していくことで、**エラーに強い堅牢なコード**を書くことが出来るということが分かりました。
慣れないうちは大変かと思いますが、うまく活用できれば今後の自身の開発をスムーズに進めてくれるツールになると信じて頑張ります。

当記事は、今後も自身の成長に合わせてアップデートしていくつもりです。
何か修正箇所などございましたら、お知らせいただけると幸いです。

最後までお読みいただき、ありがとうございました!



#参考

https://www.udemy.com/share/103jMOAEAbdVlRQXw=/