🤖

TypeScript入門 【ちょっと発展した型定義編】

2021/10/25に公開

今回はTypeScriptのちょっと発展した型定義について説明していきます。

型エイリアス vs インターフェース


まずは似ているけれど違う、型エイリアスとインターフェースについて説明していきます。

大きな違いは主に2つです。

まず、インターフェースは拡張に対してオープンな性質を持っています。

2つ目は、型エイリアスはマップ型や条件付き型といった高度な型構文が記述できるけれど、インターフェースではそれができないというところになります。


interface hoge {
  sideA: number;
  sideB: number;
}
// 型が拡張されていく
interface hoge {
  sideC: number;
}
const obj: hoge = {
  sideA: 1,
  sideB: 2,
  sideC: 3
};

type bar = {
  sideA: number;
  sideB: number;
};
// エラー 'bar' is already defined.
type bar = {
  sideC: number;
};

// interfaseにはこの表現はできない
type numbers = 1 | 2 | 3;

この2つは型エイリアスの方が便利で使いやすいので、特に理由がなければ型エイリアスを使えば良いと思います。

後ほど紹介するようにtypeも拡張することは可能ですし。

型の演算子


次に型の演算子について紹介していきます。

これらを使うことで、さらに高度な型を定義できるようになります。

共用体型 ユニオン型

共用体型とは、演算子 | で型を並べることで、それらの内のいずれかの型が適用される複合的な型にさせる方法です。


// 1か2か3が入る
type hoge = 1 | 2 | 3;

type A = {
  foo: number;
  bar?: string;
};
type B = { foo: string };
type C = { baz: boolean };
type AorB = A | B; // { foo: number | string; bar?: string }
type AorC = A | C; // { foo: number; bar?: string } or { baz: boolean }
const typeA: AorB = { foo: "a" };
const typeB: AorB = { foo: 1, bar: "a" };
// エラー Type 'number' is not assignable to type 'string | undefined'.
const typeD: AorB = { foo: 1, bar: 2 };

オブジェクト型も共用体型に適用でき、基本は文字列リテラル型と同じで 単にその並べられたオブジェクトの型のいずれかが適用されるという感じになります。

enum型

ついでにenum型というものも解説します。

こちらは別名列挙型と言い、変数の値を制限することができます。

実際のコードを見た方が早いと思うので、次の通りになります。

// 任意の数字や文字列を割り当てることもできます
enum MOBILE_OS {
  IOS = 'iOS',
  ANDROID = 'Android'
}

// 以下のように、型として扱うことも可能です
const os: MOBILE_OS = MOBILE_OS.IOS
// こちらはエラーになります
const os: MOBILE_OS = 'iOS'

交差型 インターセクション型

交差型とは演算子&で並べ、『A かつ B』と複数の型をひとつに結合させるものになります。

用途としては、オブジェクト型の合成に使われます。

この時同じ型でありながら必須と省略可能が交差したら、必須のほうが優先されます。

そして、もし同じプロパティで型が共通点のないものだった場合は、never型になります。


type A = { foo: number };
type B = { bar: string };
type C = {
  foo?: number;
  baz: boolean;
};
type AnB = A & B; // { foo: number, bar: string }
type AnC = A & C; // { foo: number, baz: boolean }
type CnAorB = C & (A | B); // { foo: number, baz: boolean } or { foo?: number, bar: string, baz: boolean }

typeof

次にtypeof演算子です。

これは型のコンテキストで用いると、変数から型を抽出してくれます。


const numArr = [1, 2, 3];
// number[]になる
const newArr: typeof numArr = [1];

keyof

次はkeyof演算子です。

これは通常の式では使えず、型コンテキストのみで用いられる演算子になります。

これは文字通りオブジェクトの型からキーを抜き出してくるます。


const permissions = {
  lastName: "hinohara",
  wfirstName: "shinya"
};
type PermsChar = keyof typeof permissions; // 'lastName' | 'firstName'
const readable: PermsChar = "lastName";

このようにtypeof と合わせると、既存のオブジェクトからキーの型を抽出できます。

配列から型を抜き出す。

最後に、配列から型を作る方法を紹介します。


const hoby = ["reading", "traveling"] as const;
type hobytype = typeof hoby[number]; // 'reading' | 'traveling'

このように書くことで配列の要素をリテラル型で使えるよになります。

as const

一応補足としてas constについて説明しておきます。

as constは、const assertionを行うための記法です。

具体的には、以下のような意味を持ちます。

  • Wideningされない
  • オブジェクトリテラルのプロパティはreadonlyになる
  • 配列リテラルはredonlyのタプル型になる

Wideningとはリテラル型が自動的に対応するプリミティブ型に広げられて推論されることを指します。

例えば以下のような例を見ると分かりやすいかと思います。

let logLevels = 'hoge'; // string
let logLevels = 'hoge'; // hoge

つまり、as constを使うことで型をプリミティブ型に広げられて推論されることを防ぐことができます。

以上が、as constについての補足でした。

おわりに


今回はちょっと発展した型定義の方法について見てきました。

ここら辺までを理解できていたら、実務でもやっていけると思います。

忘れたらまたこの記事に戻ってきていただければと思います。

最後に宣伝です。

0からエンジニアになるためのノウハウをブログで発信しています。
https://hinoshin-blog.com/

また、YouTubeでの動画解説も始めました。
YouTubeのvideoIDが不正ですhttps://www.youtube.com/channel/UCqaBUPxazAcXaGSNbky1y4g

インスタの発信も細々とやっています。
https://www.instagram.com/hinoshin_enginner/

興味がある方は、ぜひリンクをクリックして確認してみてください!

おわり

Discussion