コンテンツにスキップ

TypeScript

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. TypeScriptとは

静的型付けのフロントエンド言語である。

tsconfig.jsonファイルに基づいて、TypeScriptファイルをコンパイルし、JavaScriptファイルを作成する。

拡張子として、tstsx (TypeScript内にJSXを実装できる) を使用できる。


02. 型定義

プリミティブ

▼ 変数

let str: string = "hello";
// Type 'number' is not assignable to type 'string'.
str = 0;
let num: number = 0;
// Type 'string' is not assignable to type 'number'.
num = "0";
let big: bigint = 10n;
// Type 'number' is not assignable to type 'bigint'.
big = 0;
let bool: boolean = true;
// Type 'number' is not assignable to type 'boolean'.
bool = 1;
let n: null = null;
// Type 'undefined' is not assignable to type 'null'.
n = undefined;
let u: undefined = undefined;
// Type 'null' is not assignable to type 'undefined'.
u = null;
let sym: symbol = Symbol();
// Type 'string' is not assignable to type 'symbol'.
sym = "";


オブジェクト

▼ 変数

const object: {name: string; age: number} = {name: "taro", age: 20};

// Type 'number' is not assignable to type 'string'.
object.name = 20;

// Type 'string' is not assignable to type 'number'.
object.age = "taro";

// Property 'gender' does not exist on type '{ name: string; age: number; }'.
object.gender = "male";


配列

▼ 変数

const strArray: string[] = ["a", "b", "c"];

// Argument of type 'number' is not assignable to parameter of type 'string'.
strArray.push(0);
const numArray: number[] = [1, 2, 3];

// Argument of type 'string' is not assignable to parameter of type 'number'.
numArray.push("a");


ジェネリクス

▼ 変数

// string[] と同じ
const str: Array<string> = ["a", "b", "c"];
// (string|number)[] と同じ
const strOrNum: Array<string | number> = ["a", "b", 1, 2];
// string{} と同じ
const str: Map<string> = {a: "a", b: "b", c: "c"};

▼ 返却値

// Promise<型>
async function asyncFn(): Promise<string> {
  // 非同期処理
  return "executed";
}

console.log(await asyncFn());

▼ 型変数

型変数では、定義した時点で型が決まっていない。

コール時に型変数に任意の型を代入すると、それに合わせた引数と返却値の型の関数を定義できる。

// この時点では、型変数 (T、U) の型は決まっていない
const addKeys = <T, U>(key1: T, key2: U): Array<T | U> => {
  return [key1, key2];
};

// 型変数に型を代入すると、それに合わせた処理になる
addKeys<string, string>("a", "b");

addKeys<number, number>(1, 2);

addKeys<boolean, boolean>(true, false);

addKeys<string, number>("a", 1);


数値

▼ 引数

const sum = (x: number, y: number) => {
  return x + y;
};

console.log(sum(1, 2));

console.log(sum(1, "2")); // Argument of type 'string' is not assignable to parameter of type 'number'.

console.log(sum(1)); // Expected 2 arguments, but got 1.

▼ 返却値

const sum = (x: number, y: number): number => {
  return x + y;
};


返却値なし

// 返却値がないという型
const logger = (): void => {
  console.log("log");
};


03. 型推論

暗黙的

let name = "John"; // 変数nameは文字列として推論されます
let age = 30; // 変数ageは数値として推論されます
let isProgrammer = true; // 変数isProgrammerはブール値として推論されます


明示的

let name: string = "John";
let age: number = 30;
let isProgrammer: boolean = true;


04. 環境変数の定義

出力

▼ 言語の実行環境

  • exportコマンドで出力する
  • コンテナの環境変数として出力する

▼ dotenvパッケージ

dotenvパッケージ

なお、依存パッケージが増えてしまうため、代替の方法があるならそちらの方が良い。

import dotenv from "dotenv";

// .envファイルを読み込む
dotenv.config();

// なんらかの実装


型の定義

interface Env {
  DATABASE_NAME: string;
  DATABASE_PORT?: number;
}

const myEnv: Env = {
  DATABASE_NAME: process.env.DATABASE_NAME || "",
  DATABASE_PORT: process.env.DATABASE_PORT
    ? parseInt(process.env.DATABASE_PORT)
    : undefined,
};


エラーハンドリング

▼ 独自エラーオブジェクトの定義

ステータスコードに応じたエラーを継承すると、try-catch句で扱いやすくなる。

export class NotFoundError extends Error {
  status: number;

  constructor(message = "The Requested URL was not found on this server") {
    super(message);
    this.name = "NotFoundError";
    this.status = 404;
  }
}

▼ エラーメッセージの取得

TypeScriptでは、エラーの構造がさまざまある。

型安全のために、これらを条件分岐で処置する必要がある。

// Remixの場合
import {Response} from "@remix-run/node";

/**
 * エラーの構造に応じてエラーメッセージを取得する
 */
export async function getErrorMessage(error: unknown): Promise<string> {
  // RemixのResponseオブジェクトの場合
  // 例:スローしたjson関数によるエラーを捕捉した場合
  if (error instanceof Response) {
    try {
      const body = await error.json();
      if (typeof body?.message === "string") {
        return body.message;
      }
      if (typeof body?.error === "string") {
        return body.error;
      }
      if (error.statusText) {
        return error.statusText;
      }
      return "An unexpected error occurred.";
    } catch {
      if (error.statusText) {
        return error.statusText;
      }
      return "An unexpected error occurred.";
    }
  }

  // Typescript組み込みのErrorオブジェクトの場合
  // 例:Remixの内部的なエラーを捕捉した場合
  if (error instanceof Error) {
    if (error.message) {
      return error.message;
    }
    return "An unexpected error occurred.";
  }

  // その他の場合
  // 例:null、string、number、objectなどの想定外のエラーを捕捉した場合
  return String(error);
}
// Remixの場合
export const action = async ({request, params}: ActionArgs) => {
  try {
    // リクエストハンドリング
  } catch (error) {
    const errorMessage = await getErrorMessage(error);
    console.error("An error occurred:", errorMessage);
  }
};