Hajimeの妄言とTechの部屋

このブログでは、テック系の話や、ドキュメントに関する話などをエンジニアが「こんな感じに使うとええんじゃね?」ということを書き連ねるブログです。

React × Next.jsと学んできたので、TypeScriptもキャッチアップしてみた

ご挨拶

皆さん、こんにちは。
前回、前々回と、React × Next.jsのご紹介をしました。

dtm3110.hatenablog.com

dtm3110.hatenablog.com

今回はその第3段として、TypeScriptに関してご紹介します。

TypeScriptとは?

TypeScriptは、JavascriptをDDD(Data Driven Development)の文脈で開発する際にとても便利なツールです。
データを取り扱う際、そのデータの型を厳格に定義しておくことで、読みやすさ、リファクタリングのしやすさ、エラー・バグの抑制といった様々な利益を享受することができます

型の定義方法

InterfaceType

型を定義する際、intercacetypeという2つの方法があります
詳細は↓のQiitaの記事がとても良くまとまっていたので、ぜひ参照してみてください TypeScriptのInterfaceとTypeの比較 - Qiita

まとめのtableだけパクリスペクトさせていただきました↓

Interface Type
用途 クラスやオブジェクトの規格を定義 型や型の組み合わせに別名を付ける
継承 交差型で同じことができる
同名要素
宣言
マージされる エラー
Classへの
implement
交差型
共用体型
タプル型
Mapped Types
規定しない
プロパティ
他にもプロパティが
存在しうるものとして扱う
存在しないものとして扱う
スコープ 宣言されるとGlobalへ exportしない限りプライベートでしか使われない

Interface構文は初期のTypeScriptから使われている構文であり、逆にtype構文は比較的新しい記載方法です。
プロジェクトにもよるかと思いますが、最近はType構文で統一されることが多いように思われます。
スコープがプライベートなので、Typeのほうがルール決めして運用しやすいこともよく使われる要因でしょう。

d.tsファイル

型の定義を外出しにしたファイル

export type User = {
  id: number
  name: string
  email: string
}

export type UserRole = 'Admin' | 'Manager' | 'User'

型の種類

基本的&直感的にわかりやすいものリスト

Type Example 備考
string typeof "text" === "string" "String"じゃないことに注意!
number typeof 10 === "number" こちらも"Nmber"じゃないことに注意!
array Array.isArray([0, 1])
  • 配列であるときに使われる。
  • これも"Array"じゃないことに注意!
object tyoeof {key: "hoge"} === "object"
  • 定義される対象がobjectであるときに使われる。
  • これも"Object"じゃないことに注意!
  • 実際はObject内のKey毎に型定義することが多い
boolean typeof true === "boolean" trueまたはfalseしか入らない
undefined typeof undefined === "undefined"
  • nullとは違うことを忘れずに
  • type hoge?: string とすると
    string型もしくはundefined型という定義になる
  • ※変数自体が定義されていないものがundefined
functions typeof () => { true } === "number"
  • 指定した要素がfunctionであるときに使う
  • HandlerなどでFunctionが帰ってくるときなどに使われる
null typeof null === "null" undefinedとは違って、定義されているがデータがnull

union型

特定の型が唯一には決まってないけれど、取りうる型が決まっている場合に用いる型定義
複合型と言われることもある

const union: string | number = "foo";
const unionMulti: string | number | null = null;

タプル型

JavascriptにはそもそもTupleっていう型はないんですが、Arrayを使うことにより、TypeScriptでも宣言できるようになっています。
※ちなみに、Tuple型はLisp, Python, Haskell, C#などで使われる型です

const tuple: [string, number] = ["foo", 3]; // Note: OK!

const tuple: [number, string] = ["foo", 3]; // Note: NG!! → [3, "number"]

any型

とりあえずどんな型でもいいよっていう型
TypeScriptの思想を壊しかねないので、本当に「使わないと実装できない!!」ってとき以外使わないようにしましょう

void型

typeof null === "null"
または
typeof undefined === "undefined" | 型が存在しない場合の型
nullとundefinedのみ
const voidType: null | undefined と同義 |

〇〇の配列

公式ドキュメントでは明示的に記載されてませんが、↓のような形を宣言する際に利用されたりすることもあります

const stringArray: string[] = ["aaaa", "bbbb"]

interface customObject {
  name:string;
  id: number
}
const objectArray: customObject[] = [
  { name: "Hajime", id: 1 },
  { name: "hoge", id: 2 }
]

また、上記のような方法ではなく、次に示すGenerics型で記載する場合もあります

Generics

変数を渡すことによって、推論する型を指定できる方式

// NOTE: Arrayはデフォルトで用意されているgenerics型で、`<>`内に指定された型の配列を定義する
type stringArray = Array<string>
const arr: stringArray = ["aaaa", "bbbb"]
interfase Backpack<Type> {
  add: (obj: Type) => void;  // Note: 引数に指定された型(Type)を取り、Voidを返す関数
  get: () => Type;           // Note: 指定された型(Type)を返す関数
}
// Note declareは初期値を指定しないで宣言するときに使うTypeScriptの宣言構文
declare const backpackString: Backpack<string>;
backpackString.add("hoge");
console.log(typeof backpackString.get() === "string");

declare const backpackNumber: Backpack<number>;
backpackNumber.add(28);
console.log(typeof backpackNumber.get() === "number");

その他

ほかにもEnum型やUnknown型、Never型などがあります
詳しくは↓こちら

www.typescriptlang.org

振り返りテスト

f:id:DTM3110:20210218114350p:plain 学んだことがきちんと身になってるかどうかを確認するための、簡単な型定義テストを作ってみました。
ご興味のある方はぜひやってみてください!

Q1: 次の変数に当てはまる型UserをType構文で記述せよ

定義したい変数

const user: User = {
  id: 0,
  name: 'Hajime Saito'
}

回答&解説 (CLICK HERE!)

// 1点の回答
type User = object

// 3点の回答
type User = {
  id: number,
  name: string
}

回答は上記の通り。
変数userは確かにobjectの型ですが、Object内の型の定義ができていないため、object型を宣言せず、Object内の各要素ごとに型を宣言してあげるのがBetterです。

Q2: 次の変数に当てはまる型UserListをType構文で記述せよ
なお、Q1で作った型を必ず利用すること

定義したい変数

const userList: UserList = [
  {
    id: 0,
    name: 'Yuji Itadori'
  },
  {
    id: 1,
    name: 'Megumi Fushiguro'
  },
  {
    id: 2,
    name: 'Satoru Gojo'
  },
]

回答&解説 (CLICK HERE!)

// 必須利用
type User = {
  id: number,
  name: string
}

// 0点の回答
type UserList = [User, User, User]

// 3点の回答
type UserList = User[]

// 3点の回答
type UserList = Array<User>

回答は上記の通り。
正直0点の回答は正解にしたくないですが、TypeScript的にはエラーを出さないので、一応0点としました。
(なお、何故正解にしたくないのかという問題がQ4になります。)

3点の回答2つについて

今回はQ1で定義されたuserを要素に持つ配列の型定義でした。
「配列の中にUserが含まれる」という定義になるので、User[]もしくはArray<User>が正解となります。
この2つの定義方法ですが、公式も同列に紹介しており、その中身的な差異はありません。
趣味で使い分けることになるでしょう(ただ、○○[]のほうが圧倒的に打込み量が少なく、楽なのでこっちを使っている場合がお多いように思います。)

Q3: 次の変数に当てはまる型UserListをType構文で記述せよ
なお、Q1で作った型は利用しないこと

定義したい変数

const userList: UserList = [
  {
    id: 3,
    name: 'Jogo'
  },
  {
    id: 4,
    name: 'Hanami'
  },
  {
    id: 5,
    name: 'Dagon'
  },
]

回答&解説 (CLICK HERE!)

// 0点の回答
type UserList = [object, object, object]
type UserList = [
  {
    id: number,
    name: string
  },
  {
    id: number,
    name: string
  },
  {
    id: number,
    name: string
  }
]
// 1点の回答
type UserList = object[]
type UserList = Array<object>

// 3点の回答
type UserList = {
  id: number,
  name: string
}[]

// 3点の回答
type UserList = Array<{
  id: number,
  name: string
}>

回答は上記の通り。
0点の回答は一応TypeScript的には通ってしまうので、回答として追加しましたが、個人的に嫌いなので0点としてます。
1点の回答はQ1での解説と同様になるのですが一応コンパクトにかけているので、お情けで1点としています。
3点の回答はQ2での回答に対してQ1の回答を適応する形です。
基本的に3点の形で書くようにしたほうが良いでしょう。

Q4: Q2, Q3で0点となった次の2つの型定義に関して、語句を埋めよ

おすすめされない型定義

type UserList = [User, User, User]

配列の宣言において上記のような宣言をした場合、厳密には配列(Array)の型宣言ではなく【AAAAA】型の型定義である。
なので、UserListを用いて変数userListを宣言した場合【BBBBB】つ目の要素を追加したときにエラーが発生する。
また【AAAAA】型なので、下記のようにUserList2を定義すると変数hogeListにUserList2の型を当てて定義することができる。

type UserList = [User, User, User, 【CCCCC】]
const hogeLIst = [
  {
    id: 6,
    name: 'Kento Nanami'
  },
  {
    id: 7,
    name: 'Aoi Todo'
  },
  {
    id: 8,
    name: 'Suguru Getou'
  },
  9
]

回答&解説 (CLICK HERE!)

【AAAAA】: タプル 【BBBBB】: 4 【CCCCC】: number

回答は上記の通り。
解説は文章がそのまま解説になっています。
もちろん、タプル型の宣言になっているので、Arrayっぽく書いても要素数は固定なので、配列としては使えません。
また、デフォルトのデータの要素数が1の場合、[User]としてしまっても、一応通ってしまうので、デバッグするときにとても厄介です。
なので、配列の型宣言をするときは、「タプル形で書いちゃってないかな?」というのを一度立ち止まって確認してみることをおすすめします。

最後に

今回はTypeScriptの紹介ということで、各種型に関して簡単に紹介した後に振り返りテストと言う形でご紹介しました。
「これどんな型だったっけ?」というときに確認するツールとして利用していただけると嬉しいです。

今回、React, Next.js, TypeScriptと3つのキャッチアップを実施しました。
次の記事では、↑これらのキャッチアップの実施理由と、それに伴う開発までの記事を、下記ブログで1つ書こうと思っています。

tech-blog.abeja.asia

ぜひお楽しみに!