関連

Tというジェネリック型がIFooならA、違うならB」というように与えられた型によって分岐した結果の型を返すことができます。

基本

例えばこれはこのような形です。

/**
 * P(arent), C(hild)
 */
type T = C extends P ? U : V;

これはCPの拡張形式ならU型に、違うならV型になるという意味になります。

同じプロパティを持ってればtrueな感じ、なので継承はtrue

class P {}
class C {}
// type T = U

class P {
  foo: any;
}
class C extends P {}
// type T = U

class P {
  foo: any;
}
class C {}
// type T = V

Generics で複数の型を提供する

if-else-if-elseのように 2 つ以上の条件も設定できます。

これは属すからtrueな感じ

enum StatusCode {
  BadRequest = 400,
  NotFound = 404,
  InternalServerError = 500
}
/** @alias */
type Code = StatusCode;

interface BadRequestResponse {
  code: Code.BadRequest;
  fields: any[];
}

interface NotFoundResponse {
  code: Code.NotFound;
  errors: any[];
}

interface InternalServerErrorResponse {
  code: Code.InternalServerError;
  message: any;
}

type Response = Code.BadRequest extends C
  ? BadRequestResponse
  : Code.NotFound extends C
  ? NotFoundResponse
  : InternalServerErrorResponse;

type A = Response;
// type A = NotFoundResponse
type B = Response;
// type B = InternalServerErrorResponse

Generic Type の再帰はできない

以下のReturnTypeRecursiveは関数の場合は再帰的に結果を辿っていって最終的な結果の型を取得しようと思ったものですが、これは「循環参照してますよ(Type alias 'ReturnTypeRecursive' circularly references itself.ts(2456))」エラーを起こします。

type ReturnTypeRecursive<
  Fn extends (...args: any[]) => any
> = ReturnType extends (...args: any[]) => any
  ? ReturnTypeRecursive
  : ReturnType;

Generic じゃないなら再帰できるようです

type AFunction = (...args: any[]) => any | AFunction;

ネストを深くすればそのようなことはできる

ネストが浅いのであれば使用したいケースまでの再帰ライクに定義するといった使い方は可能です。

export type ReturnTypeRecursive = ReturnType<
  Fn
> extends AFunction
  ? ReturnType> extends AFunction
    ? ReturnType>> extends AFunction
      ? never
      : ReturnType>>
    : ReturnType>
  : ReturnType;
  
const aFn = () => () => () => 'foo' as const;
type A = ReturnTypeRecursive;
// type A = "foo"

infer で一部分の型を取得する

以下は関数の戻り値を取得できます。inferはパターンマッチみたく、その場の型を条件結果の部分で使うことができます。

type ReturnType = T extends (...args: any[]) => infer F
  ? F
  : never;
type A = ReturnType<() => number>;
// type A = number;

実はReturnTypeは最初から定義されてるので実装はいりません。

type A = ReturnType;

例えば、第 2 引数の型が取りたい場合は、引数の型指定部分でinferを使います。

// tslint:disable:no-unused
type SecondArg = T extends (
  a1: any,
  //  F が [tslint] TypeParameter 'F' is unused. [no-unused]
  // になってしまうので tslint:disable してます
  a2: infer F,
  ...args: any[]
) => any
  ? F
  : never;
// tslint:enable:no-unused
type A = SecondArg<(_a: number, _b: string, _c: boolean) => {}>;
// type A = string

JavaScript で飯食べたい歴約 5 年、 純( nju33 ) によるノートサイトです。

このサイトではドリンク代や奨学金返済の為、広告などを貼らせて頂いてますがご了承ください。

Change Log