Masayan tech blog.

  1. ブログ記事一覧>
  2. TypeScriptの基本的な型定義とメリット・使用例を紹介する

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

Typescriptの何がうれしいのか

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

型推論

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

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などでエラーが出る様子)

// 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で書いているのと同じになってしまうので、基本使わないこと

まとめ

いかがでしたでしょうか。本記事では、TypeScriptの基本的な型定義とメリット・使用例について紹介しています。フロントエンドも型を付与することでより堅牢なシステムを構築することが可能になるので、ぜひ参考にして使用してみてください。