TypeScriptの基本的な型定義とメリット・使用例を紹介する

環境

  • windows10
  • DockerDesktop for Win 3.5.x
  • Laravel 8.x
  • PHP 8.x
  • node 16.13.1
  • npm 8.1.2
  • React 17.0.1
  • VsCode
  • gitbash 2.32.0.1

Tsの何がうれしいのか

  • 型が付与されていると、エディター上でいろいろな補完が効くようになる
    例えば、配列の型を付与していると、配列に関するメソッドがサジェストされる等
  • 型を付与することにより、予期せぬエラーやバグの可能性を軽減でき、堅牢なプログラムを作成できる

型推論

基本的には型推論が効くので、明示的に型注釈(タイプアノテーション)することが不要な場合は、型の記述を省略できる

Typescriptの型の基本

型の種類

プリミティブ

string
  • 文字列
// react state
const [barcode, setBarCode] = useState<String>('')

// 変数
const hoge: String = "hoge";
number
  • あらゆる数値
  • 負の値でも整数、浮動小数点数でもすべてnumber

※書き方は文字列型と同じなのでそちらを参照ください

boolean
  • 真偽値

※書き方は文字列型と同じなのでそちらを参照ください

関数

引数と返り値の型
  • パラメーターは型推論不可(型の指定がないと、anyになる)
  • 返り値は型推論可
  • 返り値がない場合はvoidを指定する
  • アロー関数のパラメーターが1つでも()で囲ってあげる必要あり
// 通常の関数
function func1(a: number, b: number): number {
  return a + b;
}

// 無名関数
const anonymous1 = function (a: number, b: number): number {
  return a + b;
};

// アロー関数
const func2 = (a: number, b: number, c?: number): number => a + b;

// アロー関数の無名関数
const anonymous2: (a: number, b: number) => number = func2;
  • 関数の戻り値が関数の場合
    どのような関数なのかを型として定義し、それを指定する
// useHandleLoginは返り値がvoidの関数を返す関数
// handleLoginはstringの引数を2つ受け取って、voidを返す関数
export const useHandleLogin: () => void = () => {
  const handleLogin: (email: string, password: string) => void = (email, password) => {
  ...割愛(返り値はvoid)
  })
  return {
    handleLogin
  }
}

配列

通常の配列
  • 型のあとに[]を付けるだけ
// 文字列型のデータが入っている配列
const arr: string[] = ["hoge", "foo"];
タプル型

配列内の各要素の型を明示的に指定することができる型

const arr: [number, string, boolean] = [1, "hoge", true];

オブジェクト

  • typeかinterfaceを使用する
  • interfaceにはセミコロンが不要で、typeには必要
  • interfaceではオブジェクトとクラスの型だけ定義できるが、typeはオブジェクト以外の型も参照可能
  • interfaceは拡張ができるが、typeはできない(厳密にはtypeも拡張できる..)
  • 公式ドキュメントによると、interfaceを推奨しているとのこと
  • typeは型エイリアス(型の内容をエイリアスで別名管理する)
// interface
interface Book {
  id: number;
  title: string;
}

const book: Book = {
  id: 1,
  title: "カラマーゾフの兄弟",
};

// type
type Props = {
  books: {
    id: number;
    name: string;
  }[];
};

Json

jsonデータを型として適用することも可能

frontend\next-web\src\types\user\user.json

{
    "id": 1,
    "name": "",
    "email": "",
    "email_verified_at": "",
    "created_at": ""
}
import React, { useCallback, useState, useEffect, useContext } from 'react'
import axios from 'libs/axios'
import { useRouter } from 'next/router'
import { useRecoilCallback, useRecoilState } from 'recoil'
import { IUserState, userState } from 'components/store/atom/auth'
import { AxiosRequestConfig, AxiosResponse } from 'axios'
import userData from '../../../../types/user/user.json'

type USER = typeof userData

export const useHandleLogin = () => {
  const [user, setUser] = useRecoilState(userState)
  const router = useRouter()

  const handleLogin: (email: string, password: string) => void = (email, password) => {
    axios.get('/sanctum/csrf-cookie').then((response: AxiosResponse) => {
    axios
      .post('/api/login', {
        email: email,
        password: password
      })
      .then((res: AxiosResponse<USER>) => {
        const user = res.data
        setUser({
          id: user.id,
          name: user.name,
          email: user.email,
          isSignedIn: true
        })
        router.push('/')
      })
      .catch((error) => {
        console.log(error)
      })
    })
  }
  return {
    handleLogin
  }
}

Enum

  • 列挙型
  • 処理の裏側では、指定した値のオブジェクトが生成されている
enum BookType {
  BUSINESS = "business",
  FASHION = "fashion",
  MANGA = "manga",
}

interface Book {
  id: number;
  title: string;
  type: BookType; // 追加
}

const book: Book = {
  id: 1,
  title: "鬼滅の刃",
  type: BookType.MANGA,
};

Union型

  • 型版or演算子( | で型をつなぐ)
const something: string | number = 10; // 文字列も数値もOK

const array: (string | number)[] = ["hoge"]; // 配列の場合でも同じ

リテラル型

  • 文字列型(string型)や数値型、boolean型にさらに制約を加えて、 特定の文字列のみ許可する
  • ユニオン型と組み合わせると、列挙型(Enum)と同じような機能が実現できる
// 文字列型という制約に加え、その中でもsmall、medium、large以外の文字列は使えない
const bookSize: "small" | "medium" | "large" = "medium";

// ユニオン型と組み合わせると、列挙型のようになる
interface Book {
  id: number;
  title: string;
  type: BookType;
  size: "small" | "medium" | "large";
}

const bookSize: "small" | "medium" | "large" = "medium";

const book: Book = {
  id: 1,
  title: "鬼滅の刃",
  type: BookType.MANGA,
  size: bookSize,
};

型のパラメーター化

ジェネリクスについて
  • 関数/classでジェネリクスという機能が使用できる
  • 型をパラメーター化する範囲や数を柔軟に指定できる
    引数が複数あれば、それぞれの型をパラメーター化したりすることが可能
  • 以下のような、引数等の型の種類が異なるだけで、中身の処理自体は同じという場合に、ジェネリクスを使用する余地がある
const computeLength = <T,>(variable: T): T => variable.length
const computeLength2 = <T, U, P>(x: T, y: U, z: P): number => x.length + y.length + z.length

console.log(computeLength('hoge')) // 4
console.log(computeLength(['hoge', 'foo'])) // 2
console.log(computeLength2('1000', 'hoge', ['foo'])) // 9
tsxファイルでエラーになる場合の回避方法

Reactを使用していて、エディターでジェネリクスがうまく動際してくれないときは、以下のようにすることでエラーを回避できる(vscodeのインテリセンスやprettierなどでエラーが出る様子)

Generic usage reported as JSX Error · Issue #15713 · microsoft/TypeScript
TypeScript Version: 2.2.2 & 2.3.2 Code // Basic example const paramArray = <T>(param1: T, param2: T) => ; // React specific example type Component&...
// error
<T>(variable: T) => variable.length

// OK
<T,>(variable: T) => variable.length

存在しない値

null

値が割り当てられた後にもかかわらず、値がない

optional

nullを許容する場合は変数名の後に?を付与する

const func = (a: number, b: number, c?: number): number => a + b;
undefined
  • 値が割り当てられていない変数に指定する型
  • 変数初期化前に指定したりする
  • 関数の返り値にundefinedを指定することは不可(return分があれば指定可能)

何でもあり

any

any型を付与したとしても、結果的にはJavascriptで書いているのと同じになってしまうので、基本使わないこと

タイトルとURLをコピーしました