LoginSignup
56
43

More than 3 years have passed since last update.

TypeScriptのUtilityTypes、MappedTypes、ConditionalTypesのまとめ

Last updated at Posted at 2021-02-18

代表的なUtilityTypes

Partial (全てのプロパティをオプション化する)

type Item = {
  name: string
  price: number
}
type PartialItem = Partial<Item>

// PartialItemは結果、以下のような型になる。
type PartialItem = {
  name?: string
  price?: number
}

と全てのプロパティをオプショナルにできる。

Required (全てのプロパティを必須にする)


type Item2 = {
  name?: string
  price?: number
}
type RequiredItem = Required<Item2>

// RequiredItemは結果、以下のような型になる。
type RequiredItem = {
  name: string
  price: number
}

と同じように、今度は全てのプロパティを必須にする事ができる。

ReadOnly (全てのプロパティを読み取り専用にする)


type NotReadOnlyItem = {
  name: string
  price: number
}
type ReadOnlyItem = ReadOnly<NotReadOnlyItem>

// この結果、以下のように全てのプロパティはreadonlyにアップデートされます。
type ReadOnlyItem = {
  readonly name: string
  readonly price: number
}

Record (2つの型を行の型、列の型のような感じで二次元化できる型です)


type Gyou = 'A' | 'B' | 'C'
type Retsu = {
  Kokugo: number
  Sansuu: number
  Rika: number
  Shakai: number
}
const membersScore: Record<Gyou,Retsu> = {
  A: {Kokugo: 80, Sansuu: 40, Rika: 45, Shakai: 90},
  B: {Kokugo: 30, Sansuu: 90, Rika: 80, Shakai: 20},
  C: {Kokugo: 100, Sansuu: 100, Rika: 100, Shakai: 100},
}

Exclude (指定した型から特定の型を除外する)


type MyFunction = () => 'myfunction'
type VariablesType = string | number | boolean | MyFunction | null | undefined
type MyVariablesType = Exclude<VariablesType, MyFunction>

// MyVariablesTypeは以下のようになる。
type = MyVariablesType = string | number | boolean | null | undefined

// 以下のようにMyFunctionより広義のFunction型を指定して除外する事も可能
type MyVariablesType2 = Exclude<VariablesType, Function>
// 結果は同様に以下のようになる。
type = MyVariablesType2 = string | number | boolean | null | undefined

Extract (指定した型から特定の型を抽出する)


type MyFunction = () => 'myfunction'
type VariablesType = string | number | boolean | MyFunction | null | undefined
type MyFunctionType = Extract<VariablesType, MyFunction>

// MyFunctionTypeは以下のようになる。
type MyFunctionType = MyFunction

// 以下のような例でも同様の結果が得られる
type MyFunctionType2 = Extract<VariablesType, Function>
// この結果も以下のとおりである
type MyFunctionType2 = MyFunction

// またUnion型で複数指定も可能である。
type MyVariablesType = Extract<VariablesType, string | number | boolean>
// この結果は以下の通りです。
type MyVariablesType = string | number | boolean

NonNullable (指定した型からnullとundefinedを取り除く)


type VariablesType = string | number | boolean | Function | null | undefined
type NonNullableVariablesType = NonNullable<VariablesType>

// 結果は以下のようになる
type NonNullableVariablesType = string | number | boolean | Function

Pick (指定した型から指定した型だけを取り出して新しい型を定義する)


type Item = {
  name: string
  price: number
  weight: number
}
type ItemPrice = Pick<Item, 'name' | 'price'>

// ItemPriceは以下のようになる。
type ItemPrice = {
  name: string
  price: number
}

Omit (指定した型から指定した型だけを除外して新しい型を定義する)


type Item = {
  name: string
  price: number
  weight: number
}
type ItemPrice = Omit<Item, 'weight'>

// ItemPriceは以下のようになる。
type ItemPrice = {
  name: string
  price: number
}

ReturnType (関数の戻り値の型を取得する)


function multi(x: number, y:number) {
  return x * y
}
type MultiReturnType = ReturnType<typeof multi>

//結果は以下の通りです。
type MultiReturnType = number

Parameters (関数の引数の型を取得する)


const getItemInfo = (name: string, price: number) => {
  console.log({ name, price })
}
type Item = Parameters<typeof getItemInfo>
const item1: Item = ['potion', 100]
getItemInfo(...item1)

MappedTypesについて(動的に新しい型を生成できる機能)

keyof


type Item = {
  name: string
  price: number
  weight: number
}
type ItemPropertyTypes = keyof Item

// 以下が結果
type ItemPropertyTypes = 'name' | 'price' | 'weight'

in

UtilityTypsのPartialとRequiredが内部でどのように定義されているかを見てみましょう。


type Partial<T> = {
  [P in keyof T]?: T[P]
}
// TはPartialに渡された型(引数のような感じ)でkeyof Tは1つ前のkeyofの項で説明済みのように
// keyof TでT型のプロパティの文字列リテラル型のUnion型で返却されます。
// inはkeyof Tで取り出された型を1つずつ取り出す事ができ、Pに代入されます。
// 最後に?:によって、それぞれの型をオプショナルな型としT[P]でT型のPプロパティ型として定義されるという仕組みです。
// 以下参考までにRequiredをみてみます。

type Required<T> = {
  [P in keyof T]-?: T[P]
}

// Partialとの違いは-?:の部分です。もし該当のプロパティが?付きのオプショナルプロパティだった場合は
// -?で?を取り除く事でオプショナルを解除し、必須項目とするという事になります。

ConditionalTypesについて(条件付きで動的に新しい型を生成できる機能)

3項演算子(?:)

UtilityTypesのExcludeが内部でどのように定義されているか見てみましょう。

type Exclude<T, U> = T extends U ? never : T

// T extends U ? never : Tの部分が3項演算子として評価されているのは雰囲気で理解できるかと思います。
// これはT型に含まれるそれぞれの型がU型と互換性があるかどうかを評価しています。
// 以下、具体的な例で見てみましょう。

type VariablesType = string | number | boolean
type StringOrNumberType = Exclude<VariablesType, boolean>

// StringOrNumberTypeは名前の通り以下のようになります。
type StringOrNumberType = string | number

// この例でExcludeの T extends Uの部分を考えてみると
// VariablesType型内のstring型がT、そしてUがbooleanになりstring型はboolean型に互換性が無いのでfalseと評価され
// ? nerver : TのTとして評価されます。Tはstring型として返却されます。
// しかしながら、VariablesType型はstring型だけではなく、number型とboolean型も含まれています。
// TをString型として評価したのと同様に、今度はTをnumber型として評価します。number型もboolean型に互換性がないのでfalseと
// 評価されて、Tが返却されます。すなわちnumber型です。
// 最後にboolean型を評価します。Tがboolean型、Uもboolean型で当然互換性はあります。
// ですのでtrueとして評価され never が返却されます。
// 最終的に返却される型は string | number | neverのUnion型ですが、never型は無いのと同じ意味合いになるので
// string | number型だけが残され、期待する結果が得られると言うわけです。
// このT型の要素を一つずつ取り出して評価する仕組みをDistributive Conditional Typesと呼びます。

Distributive Conditional Typesについて詳しく知りたい方は公式サイトをご確認ください。

infer(推論. inferはConditional Typesの中でしか利用できない。)

conditional typesの中でGenerics型を利用可能にする為のものがinferです。

// 例としてReturnTypeの構造を参考にしてみましょう。
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
// 順番に頭からみていきます。
// T extends (...args: any) => anyの部分は一言でまとめるとTは関数であるという意味になります。
// 複数のany型の引数をとって、any型を返却するもの、すなわち関数です。
// 次に=の右辺ですが、T extends (...args: any) => infer Rです。
// ここも基本的に同じく関数である事を意味していますが、ひとつだけ異なるのが返却値の部分がanyではなくinfer R
// となっている事です。通常ReturnType<T>のように型の名前の後ろにくる<>カッコの中でしかGenerics型は宣言できません。
// もしここをT extends (...args: any) => Rとしても、TSコンパイラは理解してくれません。
// ここにinferを添える事で、infer RすなわちRを推測する。という意味合いで、Generics型でRを利用可能とし
// かつ渡された関数の戻り値を推論して新しい型を返却してくれる事になります。
// 慣れないと分かりそうで、分からないinferですが、推論と読み直してもらうと理解しやすいと思います。

最後に

私は業務でTypeScriptを利用してはおりますが、TSの世界は非常に深く理解がまだまだ及んでおりません。
今回の記事を書くにあたってUdemyのはむ先生の講座を参考にさせていただきました。
https://www.udemy.com/course/ts-for-js-developers/
大変分かりやすい講座ですので、ご興味のある方は参考にしてみてください。

56
43
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
56
43