✝️

【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>からnullundefinedを除外した型を作ることが出来ます。

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

まとめ

開発中に良く分からない型がいくつか出てきていたので、まとめてみました。間違っていたらご指摘いただけると幸いです。🙇‍♀️

参照

https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type
https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html#uppercasestringtype

Discussion