フロントエンド入門者へおすすめ動画&書籍おすすめ!

TypeScript

TypeScript の高度な型 ⑤ 条件分岐で得られる確約と、ネストされた型と、部分的な型抽出

0
0

条件分岐で得られる確約

型の条件分岐が成立した場合、Indexed Access Types による型参照が可能になります。

 

次の型を例に、Deep Nest 型の dee-.nest.value の型を抽出してみます。

 

interface DeepNest {
 deep: {nest: {value: string}}
}

interface ShallowNest {
 shallow: {value: string}
}

interface Properties {
 deep: DeepNest
 shallow: ShallowNest
}

 

DeepDive型は、Properties型に含まれるプロパティのうち、DeepNest型と互換性があるプロパティに限って、

 

Salvage<T[]K>を返却します。DeepNest型と互換性がないプロパティは、never型が適用されます。

 

type Salvage<T extends DeepNest> = T['deep']['nest']['value']
type DeepNest<T> ={
 [K in typeof T]: T[K] extends DeepNest ? Salvage<T[K]> : never 
}[keyof T]
type X = DeepDive<Properties> // type X = string

 

T['deep']['nest']['value'] のようなIndexed Access が成立するのは、Salvage型のGenericsが<T extends DeepNest> と指定されており、

 

Genericsは、DeepNest型と互換性があることが確約されているためです。

 

T[K] extends DeepNest ? という構文によりT[K]はDeepNest 型と互換性があることが確約されているために、

 

Salvage<T[K]>の適用が可能になっています。

 

このように、抽出したい型が深い階層にある場合、改造型を条件分岐に用いることで、推論を深堀できます。

 

部分的な型抽出

 

Conditional Types 構文の中のみ利用できる infer シグネチャを用いると、

 

部分的な型抽出が可能になります。

 

これが、 Type Inference in Conditional Types と呼ばれる機能です。

 

組み込み Utility Types である Retrun Type 型と同じ型で、

 

この挙動を確認してみましょう。

 

T extends に続く 「(...arg: any[]) => infer U 」が条件となる型です。

 

これは、「関数型かつ戻り型がある」型であることを表しています。

 

この「関数型かつ戻り型がある」条件が満たされた場合、

 

戻り型のU型を返却します。

 

function greet() {
 return 'Hello!'
}

type Return<T> = T extends(...arg: any[] => infer U ? U : never)
type R = Return<typeof greet> // type R = string 

 

引数型の抽出

function greet(name: string, age: number){
 return 'hello! Im $(name). $(age) years old'
}

type A1<T> = T extends (...arg: [infer U, ...any[]]) => any ? U : never
type A2<T> = T extends (...arg: [any, infer U, ...any[]]) => any ? U : never
type AA<T> = T extends (...arg: infer U) => any ? U : never

type X = A1<typeof greet> // type X = string
type Y = A2<typeof greet> // type Y = number
type Z = AA<typeof greet> // type Z = [string, number]

 

Promise.resolve 引数型の抽出

 

下記のコードでは、async関数は、X型で確認できることからもわかるように、Promise<String>を返す関数です。Resolve Arg型では、PromiseのGenericsにinfer Uを配置してるために、string型が導かれます。

 

async function greet(){
 return 'Hello!'
}

type ResolveArg<T> = T extends () => Promise<infer U> ? : never

type X = typeof greet // type X = () => Promise<string>
type Y = ResolveArg<typeof greet> //type Y = string

 

Promise型に限らず、このようにGenerics に対して infer シグネチャを適用できます。

Pocket
LinkedIn にシェア

フロントエンド入門者へおすすめ動画&書籍おすすめ!

  • この記事を書いた人
  • 最新記事

藤沢瞭介(Ryosuke Hujisawa)

りょすけと申します。18歳からプログラミングをはじめ、今はフロントエンドでReactを書いたり、AIの勉強を頑張っています。off.tokyoでは、ハイテクやガジェット、それからプログラミングに関する情報まで、エンジニアに役立つ情報を日々発信しています!

-TypeScript

Copyright© off.tokyo , 2021 All Rights Reserved Powered by AFFINGER5.