442
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

【TypeScript】Utility Typesをまとめて理解する

Utility Typesとは

Utility Typesとはコード内で型変換を容易にする為にTypeScriptが提供する(便利な関数のような)型達です。

Partial<T>

Partial<T>Tの全てのプロパティをOptional(任意)のプロパティにしてくれます。
下記のコードPersonでは1つも任意のプロパティが設定されていませんが、Partial<T>を使用する事で、変数taroではfirstNameのみを持つ事ができています。

interface Person {
  firstName: string
  lastName: string
  age: number
}

const taro: Partial<Person> = {
  firstName: 'Taro',
}

console.log(taro); //=> { firstName: 'Taro' }

Required<T>

Required<T>Tの全てのプロパティを必須のプロパティにしてくれます。
下記のコードPersonでは全てが任意のプロパティが設定されていますが、Required<T>を使用する事で、変数jiroではfirstNamelastNameの両方を与えてあげないと、エラーが発生します。

interface Person {
  firstName?: string
  lastName?: string
}

const jiro: Required<Person> = {
  firstName: 'Taro',
} 
// Error: property 'lastName' is missing.

Readonly<T>

Readonly<T>Tの全てのプロパティをreadonlyのプロパティにしてくれます。
下記ではReadonly<Todo>としているため、Todoのオブジェクトであるtodotitleを更新しようとするとエラーが発生します。

interface Todo {
  title: string
}
const todo: Readonly<Todo> = {
  title: '家の掃除',
}
todo.title = '夕飯の買い出し' // Cannot assign to 'title' because it is a read-only property.ts(2540)

Record<K, T>

Record<K, T>KがプロパティとなりT型を持つレコード型を構築します。
言葉ではややわかりずらいので、下記の例を元に考えています。
 
Record<K, T>Kにはnumberが指定されています。そして、TにはUserが指定されています。
このことから、Record<K.T> を使用して作成されたUserList型はnumber型がプロパティとなり、Userをもつ型となります。

interface User {
  name: string
}
type UserList = Record<number, User>

const list: UserList = { 0: { name: 'Taro' }, 1: { name: 'Jiro' } }
console.log(list) //=> { '0': { name: 'Taro' }, '1': { name: 'Jiro' } }

Pick<T,K>

Pick<T,K>は既に存在するT型の中からKで選択した一部のプロパティのみを含んだ新たな型を構築します。
下記の例では、Todotitlecompletedのみを含んだ新たな型TodoPreviewを作成しています。

interface Todo {
  title: string
  description: string
  completed: boolean
}
type TodoPreview = Pick<Todo, 'title' | 'completed'>

const preview: TodoPreview = {
  title: '部屋の掃除',
  completed: false,
}
console.log(preview) //=> { title: '部屋の掃除', completed: false }

Omit<T,K>

Omit<T,K>は既に存在するT型の中からKで選択した一部のプロパティを除いた新たな型を構築します。
下記の例では、Todoからdescriptionを抜き取って、新たな型TodoPreviewを作成しています。

interface Todo {
  title: string
  description: string
  completed: boolean
}
type TodoPreview = Omit<Todo, 'description'>

const preview: TodoPreview = {
  title: '部屋の掃除',
  completed: false,
}
console.log(preview) //=> { title: '部屋の掃除', completed: false }

Exclude<T,U>

Exclude はT型のプロパティでU型に代入可能なプロパティを取り除いた型を構築します。
下記の例では、
ExcludedTypeでは"id" | "name"から"id" | "address"に代入可能な"id"を取り除いた型を作成しています。
ExcludedType2ではstringnumberに代入可能では無いので、stringとなります。
ExcludedType3ではstring | number | booleanで唯一string | booleanに代入可能では無いnumberとなります。

interface TypeA {
  id: number
  name: string
}
interface TypeB {
  id: number
  address: string
}

type ExcludedType = Exclude<keyof TypeA, keyof TypeB> // "name"
type ExcludedType2 = Exclude<string, number> // string
type ExcludedType3 = Exclude<string | number | boolean, string | boolean> // number

Extract<T,U>

Extract<T,U>Exclude<T,U>とは反対でT型のプロパティでU型に代入可能なプロパティのみを残した型を構築します。
下記の例では、
ExtractedTypeでは"id" | "name"から"id" | "address"に代入可能な"id"のみ残した型を構築します。
ExtractedType2では代入可能な型は存在しない無いので、neverとなります。
ExtractedType3ではstring | number | booleanstring | booleanが代入可能なのでstring | booleanとなります。

interface TypeA {
  id: number
  name: string
}
interface TypeB {
  id: number
  address: string
}

type ExtractedType = Extract<keyof TypeA, keyof TypeB> // "id"
type ExtractedType2 = Extract<string, number> // never
type ExtractedType3 = Extract<string | number | boolean, string | boolean> // string | boolean

NonNullable<T>

NonNullable<T>Tからnullundefinedを取り除いた型を構築します。
下記の例では、
TypeAstring | number | undefinedからundefinedを取り除いた, string | numberになります。
TypeBstring[] | null | undefinedからnull | undefinedを取り除いた, string[]になります。

type TypeA = NonNullable<string | number | undefined> // string | number
type TypeB = NonNullable<string[] | null | undefined> // string[]

Parameters<T>

Parameters<T>は関数型Tの引数の型をタプル型として抽出した型を構築します。

const func = (text: string, num: number) => {
  return `${text}${num}`
}

type TypeA = Parameters<typeof func> // [string, number]

ConstructorParameters<T>

ConstructorParameters<T>T型のコンストラクタの引数の型をタプルとして抽出した型を構築します。

class Person {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}
type TypeA = ConstructorParameters<typeof Person> // [string, number]

ReturnType<T>

ReturnType<T>は関数型Tの戻り値の型を返します。
下記ではfunc2の戻り値の型はnumberなので、TypeAnumberとなります。

const func2 = (): number => 1
type TypeA = ReturnType<typeof func2> // number

ThisParameterType

関数型Tthis引数の型を抽出した型を構築します。
--strictFunctionTypesが設定されている時のみ、期待通りの動きをします。詳細はこちら

function toHex(this: Number) {
  return this.toString(16)
}
type TypeA = ThisParameterType<typeof toHex> //Number

OmitThisParameter

関数型Tthis引数の型を取り除いた型を構築します。
下記の例ではTypeAtoHexからthis引数を除いた関数型()=> stringを返します。
--strictFunctionTypesが設定されている時のみ、期待通りの動きをします。詳細はこちら

function toHex(this: Number) {
  return this.toString(16)
}
type TypeA = OmitThisParameter<typeof toHex> // () => string

ThisType<T>

ThisType<T>を使用する事で、関数の中で参照されるthisの型をTとてコントロールする事ができます。
-noImplicitThisがが設定されている必要があります。

interface User {
  name: string
}

interface Greetings {
  hello(): void
}

const user: Greetings & ThisType<User> = {
  hello() {
    console.log(`${this.name}です。`)
  },
}
user.hello()

詳しくはstackoverflowのWhat is TypeScript's ThisType used for?でわかりやすく説明されていました。

参照

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
442
Help us understand the problem. What are the problem?