ts.svg

【TypeScript】型定義(Type Arias)

TypeScript

Type Arias とは

TypeScript では、ユニオン型やリテラル型、オブジェクトなどさまざまな型の宣言方法があります。

const union: string | number | null = 0
const reteral: 'Taro' | 'Jiro' | 'Saburo' = 'Taro'
const user: {
  id: number
  name: string
} = {
  id: 1,
  name: 'Taro',
}

しかし、これらを何度も使用するときに同じ宣言をするのは、非常に面倒です。

そのような時に使用するのが、Type Arias です。 これは型の宣言に対し、typeを用いることで名前付けをすることができます。

type Union = string | number | null
const union: Union = 0

type Reteral = 'Taro' | 'Jiro' | 'Sabro'
const reteral: Reteral = 'Taro'

type User = {
  id: number
  name: string
}
const user: User = {
  id: 1,
  name: 'Taro',
}

オブジェクト定義

オブジェクトの定義について、少しだけ補足をしておきます。

任意プロパティ

通常、オブジェクトで定義したプロパティは必須ですが、?を付けることで任意にすることができます。

type User = {
  id: number
  name?: string
}

const user1: User = {
  //OK
  id: 1,
  name: 'Taro',
}
const user2: User = {
  //OK
  id: 1,
}

readonly

readonlyを付けることで、読み取り専用のプロパティとすることができます。

type User = {
  readonly id: number
  name: string
}

const user: User = {
  id: 1,
  name: 'Taro',
}

console.log(user.id) //OK
user.id = 2 //NG

オブジェクトを入れ子にする場合、readonlyしたオブジェクトのプロパティまでは有効にならないことに注意してください。

type ExUser = {
  readonly user: User
  age: number
}

const exUser: ExUser = {
  user: {
    id: 1,
    name: 'Taro',
  },
  age: 10,
}

exUser.user.id = 2 //NG: Userでreadonlyとしているため
exUser.user.name = 'Jiro' //OK
exUser.user = {
  //NG: ExUserでreadonlyとしているため
  id: 2,
  name: 'Jiro',
}

インデックス型

オブジェクトのキーは何でもよく、値の型のみ指定する場合に使用します。

type T = {
  [key: string]: string
}

上記の例であれば、stringの値であればどのようなキーでも設定できます。

const strs: T = {
  a: 'A',
  b: 'B',
  c: 'C',
}

[]の部分にはstringnumberのみ設定できます。 numberを設定した場合は、配列のように扱うことができます。 実際にはarray型ではないので、lengthなどのプロパティは使用できません。

type T = {
  [key: number]: string
}

const nums: T = ['A', 'B', 'C']
console.log(nums.length) //NG

プロパティの型参照

型[キー]とすることで、プロパティの型を参照することができます。

type User = {
  id: number
  name: string
}
type T1 = User['id'] //number
type T2 = User['name'] //string

ユニオン型

Type Arias でも、通常の型と同様にユニオン型を使用することができます。

type A = string | undefined
type B = number | undefined
type C = A | B // string | number | undefined

オブジェクトをユニオン型で指定する場合は、以下のようにプロパティがマージされます。

type A = {
  id: number
}
type B = {
  name: string
  age: number
}
type User = A | B

const user: User = {
  id: 1,
  name: 'Taro',
  age: 20,
}

すべてのプロパティを持つオブジェクトを指定する必要はなく、いずれかの型を満たすオブジェクトを指定すればよいです。

const user1: User = { id: 1 } //OK
const user2: User = { name: 'Taro', age: 20 } //OK
const user3: User = { name: 'Taro' } //NG

型の違う同じプロパティ名を持っていた場合はマージされます。 つまり、どちらの型も持つユニオン型となります。 ただし、上記で説明した通り、いずれかの型を満たさなければエラーとなります。

type A = {
  id: number
  name: string
}
type B = {
  id: string
  age: number
}
type User = A | B
const user1: User = { id: 1, name: 'Taro', age: 20 } //OK
const user2: User = { id: '1', name: 'Taro', age: 20 } //OK
const user3: User = { id: '1', name: 'Taro' } //NG

インターセクション型

インターセクション型は、型の合成を意味します。 ユニオン型はいずれかの型を満たせばよかったですが、インターセクション型はすべての型を満たす必要があります。

type A = {
  id: number
}
type B = {
  name: string
  age: number
}
type User = A & B

const user: User = {
  id: 1,
  name: 'Taro',
  age: 20,
}
const user1: User = { id: 1 } //NG
const user2: User = { name: 'Taro', age: 20 } //NG
const user3: User = { name: 'Taro' } //NG

同じキーがある場合、型が違うとエラーになります。

type A = {
  id: number
  name: string
}
type B = {
  id: string
  age: number
}
type User = A & B //NG

keyof

keyofは、オブジェクト型のキーをリテラル型として型定義することができます。

type User = {
  id: number
  name: string
  age: number
}
type UserKey = keyof User // 'id' | 'name' | 'age'
const key1: UserKey = 'id' //OK
const key2: UserKey = 'gender' //NG

typeofと組み合わせることで、オブジェクトからキーの型定義をすることができます。

const obj = {
  hoge: 'A',
  fuga: 'B',
  piyo: 'C',
}
type Keys = keyof typeof obj // 'hoge' | 'fuga' | 'piyo'

Mapped Types

Mapped Types は、オブジェクトを以下の構文によって生成する機能です。

{ [P in K]: T }

Kはキーの集合体で、stringのリテラル型である必要があります。 Tはプロパティの型となります。

まずは簡単な例を見てみます。

type T = {
  [P in 'x' | 'y']: number
}
//以下と同義
type T = {
  x: number
  y: number
}

このようにリテラル型を使用することで、プロパティが展開されます。

keyofを使用することで、既存のオブジェクト型から型を定義することができます。

type Org = {
  x: string
  y: number
}
//Orgと同じ型定義が得られる
type T = {
  [P in keyof Org]: Org[P]
}

また?readonlyを付与することが可能です。

type T = {
  readonly [P in keyof Org]?: Org[P]
}
//以下と同義
type T = {
  readonly x?: string
  readonly y?: number
}