【Typescript】Utility Typesの使いかたをまとめてみた
Utility Typesとは?
Utility Types
はTypescriptの既存の型を再利用・加工などが出来る便利な型です。
既に定義されている型と似たような型が欲しい時などにUtility Types
を使うことで、コードの重複を減らすことが出来たりし効率的に型を扱うことが出来ます。
Awaited<T>
Awaited<T>
はawait
や.then()
を使った時に得られる値の型を扱う時に使えます。
例えば非同期関数の戻り値の型が欲しい時などです。
// 基本的な構文
type Sample = Awaited<Promise<string>>
// 非同期関数の戻り値から型が欲しい時
async function fetchData(): Promise<{ name: string }>{
return { name: "sampleName" }
}
// 非同期関数の戻り値の型
type ResultFetchData = Awaited<ReturnType<typeof fetchData>>
Partial<T>
Partial<T>
は<T>
の全てのプロパティをOptional(任意)にすることが出来ます。
主にデータの更新をする際などに使用されます。
interface Person {
name: string;
age: number;
}
// Personの情報を更新する関数
function updatePerson(person: Person, updates: Partial<Person>){
return { ...person, ...updates };
}
// 1回目: 登録
const firstPerson = {
name: "first",
age: 1
}
// 2回目: 更新
const secondPerson = updatePerson(firstPerson, {
name: "second",
age: 2
})
Required<T>
Required<T>
は<T>
の全てのプロパティをRequired(必須)にすることが出来ます。
これはPartial<T>
とは反対の動きになります。
interface Person {
name?: string;
age?: number;
}
// Optionalの場合
const person: Person = {
name: "only name"
}
// ☑ nameだけでもOK
// Reuquiredの場合
const person: Required<Person> = {
name: "only name"
}
// ❌️ nameだけだとError
const person: Required<Person> = {
name: "name and age",
age: 1
}
// ☑ 全てのプロパティが代入されているのでOK
Readonly<T>
Readonly<T>
は<T>
の全てのプロパティをreadonly
(読み取り専用)にすることが出来ます。
これでプロパティの代入が禁止されるため、イミュータブルなオブジェクトとして扱えます。
interface Person {
name: string;
age: number
}
const person: Readonly<Person> = {
name: "first",
age: 1
}
person.name = "second"
// ❌️ 再代入しようとするとError
Record<K,T>
Record<K,T>
はキーの型が<K>
で、その値の型が<T>
のオブジェクト型を作ることが出来ます。
型のプロパティを別の型にマッピングする時に使用されます。
type PersonName = "first" | "second"
interface PersonInfo {
age: number;
from: string
}
// personごとの情報がある型が作れる
const people: Record<PersonName, PersonInfo> = {
first: { age: 1, from : "JP" },
second: { age: 2, from : "US" }
}
people.first // { age: 1, from: "JP" }
people.second.age // 2
Pick<T,K>
Pick<T,K>
は型<T>
から、型に含まれるプロパティ<K>
を抽出して型を作ることが出来ます。
既存の型から特定のプロパティだけを抽出して型を作りたい場合に使われます。
interface Person {
id: number
name: string;
age: number;
from: string;
email: string;
}
// 公開情報だけを抽出
type PublicPerson = Pick<Person, "name" | "from">
// 非公開情報を持たないデータの型を作れる
const person: PublicPerson = {
name: "public",
from: "JP"
}
Omit<T,K>
Omit<T,K>
は型<T>
から型に含まれるプロパティ<K>
を除外して型を作ることが出来ます。
既存の型から特定のプロパティだけを除外して型を作りたい時に使われます。
Pick<T,K>
と反対の動きになります。
interface Person {
id: number
name: string;
age: number;
from: string;
email: string;
}
// 非公開情報だけを除外
type PublicPerson = Omit<Person, "id" | "age" | "email">
// 非公開情報を持たないデータの型を作れる
const person: PublicPerson = {
name: "public",
from: "JP"
}
Exclude<T,U>
Exclude<T,U>
は元のUnion型<T>
から型<U>
を除外して型を作ることができます。
type Color = "red" | "blue" | "green"
type Color1 = Exclude<Color, "green">
// red | blue
type Color2 = Exclude<Color, "red" | "blue">
// green
type PersonInfo =
| { age: 1, from : "JP" }
| { age: 2, from : "US" }
type PersonInfo1 = Exclude<PersonInfo, { age: 1 }>
// { age: 2, from "US" }
// age:1を含んでいるものを除外
type ResultNever = Exclude<PersonInfo, { age: 1 } | { age: 2 }>
// 全て除外された場合はneverになる
Extract<T,U>
Extract<T,U>
は元のUnion型<T>
から型<U>
を抽出して型を作ることが出来ます。
Exclude<T,U>
と反対の動きになります。
type Color = "red" | "blue" | "green"
type Color1 = Extract<Color, "green">
// green
type Color2 = Extract<Color, "red" | "yellow">
// red
type PersonInfo =
| { age: 1, from : "JP" }
| { age: 2, from : "US" }
type PersonInfo1 = Extract<PersonInfo, { age: 1 }>
// { age: 1, from "JP" }
// age:1を含んでいるものを抽出
NonNullable<T>
NonNullable<T>
は<T>
からnull
とundefined
を除外した型を作ることが出来ます。
type Color0 = "red" | "blue" | "green" | null
type Color1 = "red" | "blue" | "green" | null | undefined
type Color0 = NonNullable<Color0>
// red | blue | green
type Color1 = NonNullable<Color1>
// red | blue | green
Parameters<T>
Paramters<T>
は関数型<T>
の引数の型をタプル型として抽出した型を作ることが出来ます。
function func(a: number, b:number): number {
return a+b
}
type F0 = Parameters<() => number>
// []
type F1 = Parameters<(num: number) => number>
// [num: number]
type F2 = Parameters<<T>(arg: T) => T>
// [arg: unknown]
type F3 = Parameters<typeof func>
// [a: number, b:number]
type F4 = Parameters<any>
// unknown[]
type F5 = Parameters<never>
// never
type F6 = Parameters<string>
// never Errorが出る
// 関数ではなく直接型を渡しているため
type F7 = Parameters<Function>
// never Errorが出る
// Functionは抽象的な型で具体的な引数の情報を持っていない
// そのため関数のシグネチャに一致しない
ConstructorParameters<T>
ConstuctorParameters<T>
はコンストラクタ関数型<T>
の引数の型をタプル型として抽出した型を作ることが出来ます。
type A = new (a: number, b: string) => object;
type A0 = ConstructorParameters<A>
// [a:number, b:string]
class B {
constructor(a: number, b: string) {}
}
type B0 = ConstructorParameters<typeof B>
// [a: number, b:string]
type T0 = Constructor<Function>
// never Errorが出る
// 関数を渡すとErrorがでる
ReturnType<T>
ReturnType<T>
は関数型<T>
の戻り値の型を抽出した型を作ることが出来ます。
function func(): string {
return "hello"
}
type T0 = ReturnType<() => string>
// string
type T1 = ReturnType<<T>() => T>
// unknown
type T2 = ReturnType<typeof func>
// string
type T3 = ReturnType<string>
// any Errorが出る
// 関数ではなく直接型を渡しているため
type T4 = ReturnType<Function>
// any Errorが出る
// Functionは抽象的な型で具体的な戻り値の情報を持っていない
// そのため関数のシグネチャに一致しない
InstanceType<T>
InstanceType<T>
はコンストラクタ型<T>
が生成するインスタンスの型を抽出することが出来ます。
class A {
constructor(a: number, b:string) {}
}
type A0 = InstanceType<typeof A>
// A
class B {
a = 0;
b = "hello";
}
type B0 = InstanceType<typeof B>
// B
type T0 = InstanceType<string>
// any Errorが出る
type T1 = InstanceType<Function>
//any Errorが出る
NoInfer<T>
NoInfer<T>
は<T>
の型推論を防ぐことが出来ます。
function createStreetLight<T extends string>(
colors: T[],
defaultColor?: T
) {
return {
colors,
defaultColor
}
}
type colors = "red" | "green" | "blue"
const colors: colors[] = ["red", "green", "blue"]
createStreetLight(colors, "red")
// redは含まれているのでOK
createStreetLight(colors, "yellow")
// yellowは含まれていないがErrorが出ない
// "red" | "green" | "blue" | "yellow"と推論されてしまう
第二引数の型は配列の値だけを許容したいのでdefaultColor?: NoInfer<T>
とすることで不要な型推論がされなくなる。
function createStreetLight<T extends string>(
colors: T[],
defaultColor?: NoInfer<T>
) {
...
}
createStreetLight(colors, "red")
// redは含まれているのでOK
createStreetLight(colors, "yellow")
// yellowは含まれていないのでErrorが出る
ThisParameterType<T>
ThisParameterType<T>
はthis
パラメータ<T>
の型を抽出することが出来ます。
function func(this: { a: number }, b: string){}
type T0 = ThisParameterType<typeof func>
// { a: number }
function func1(a: number, b: string){}
type T1 = ThisParameterType<typeof func1>
// unknown
OmitThisParameter<T>
OmitThisParameter<T>
は関数型<T>
からthis
パラメータを全て除外した型を作ることが出来ます。
function func1(this: { a: number }): string{
return `${this.a}year`
}
type T0 = OmitThisParameter<typeof func1>
// () => string
ThisType<T>
ThisType<T>
はオブジェクトリテラル内のメソッドにおけるthis
の型を指定することが出来ます。
// 通常の例
const obj = {
name: "test",
hello() {
// thisは暗黙的にobj型になります
console.log(`Hello ${this.name}`)
}
}
interface Person {
name: string;
age: number;
}
// ThisTypeを使用した例
type ObjMethod = {
name: string;
hello(): void;
} & ThisType<Person>
const obj2: ObjMethod = {
name: "test",
hello() {
// thisは{ name: string; age: number }型になります
console.log(`Hello ${this.name}`)
console.log(`I'm ${this.age}`)
}
}
Intrinsic String Manipulation Types
これは文字列型を操作するための型で計4種類の型があります。
Uppercase<T>
, Lowercase<T>
, Capitalize<T>
, Uncapitalize<T>
type T0 = "Hello, world"
type UpperT = Uppercase<T0>
// HELLO, WORLD
type LowerT = Lowercase<T0>
// hello, world
type CapT = Capitalize<LowerT>
// Hello, world
type UnCapT = Uncapitalize<UpperT>
// hELLO, WORLD
まとめ
開発中に良く分からない型がいくつか出てきていたので、まとめてみました。間違っていたらご指摘いただけると幸いです。🙇♀️
参照
Discussion