React × Next.jsと学んできたので、TypeScriptもキャッチアップしてみた
ご挨拶
皆さん、こんにちは。
前回、前々回と、React × Next.jsのご紹介をしました。
今回はその第3段として、TypeScriptに関してご紹介します。
TypeScriptとは?
TypeScriptは、JavascriptをDDD(Data Driven Development)の文脈で開発する際にとても便利なツールです。
データを取り扱う際、そのデータの型を厳格に定義しておくことで、読みやすさ、リファクタリングのしやすさ、エラー・バグの抑制といった様々な利益を享受することができます
型の定義方法
Interface
とType
型を定義する際、intercace
とtype
という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]) |
|
object | tyoeof {key: "hoge"} === "object" |
|
boolean | typeof true === "boolean" |
true またはfalse しか入らない |
undefined | typeof undefined === "undefined" |
|
functions | typeof () => { true } === "number" |
|
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型などがあります
詳しくは↓こちら
振り返りテスト
学んだことがきちんと身になってるかどうかを確認するための、簡単な型定義テストを作ってみました。
ご興味のある方はぜひやってみてください!
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つ書こうと思っています。
ぜひお楽しみに!