💪

TypeScriptで引数の値によって戻り値の型を変える

に公開

概要

  • 引数 flag = false に渡す値によって戻り値の型を変える

例えば以下のような関数を実装しました。

何かしらの配列をもとに User 配列を作成して返します。
User は公開情報と非公開情報をもっていて、そこから非公開情報を抜いた PublicUser を定義します。
デフォルトでは PublicUser を返し、 includePrivate 引数に true を指定した場合に User を返します。

export type User = {
  pub1: string;
  pub2: string;
  pvt1: string;
  pvt2: string
};

const pvts = ["pvt1", "pvt2"] as const;

export type PublicUser = Omit<User, (typeof pvts)[number]>;

export function parse(data: Record<string, string>[], includePrivate = false) {
    return data.map((v: Record<string, string>, i: number) => {
    const row = {
      pub1: v.data1,
      pub2: v.data2,
      pvt1: v.data3,
      pvt2: v.data4
    };

    if (!includePrivate) {
      pvts.forEach((col) => delete row[col]);
    }

    return row;
  });
}

parse([{}]);
// function parse(data: Record<string, string>[], includePrivate?: boolean): {
//   pub1: string;
//   pub2: string;
//   pvt1: string;
//   pvt2: string;
// }[]

parse([{}], false);
// function parse(data: Record<string, string>[], includePrivate?: boolean): {
//   pub1: string;
//   pub2: string;
//   pvt1: string;
//   pvt2: string;
// }[]

parse([{}], true);
// function parse(data: Record<string, string>[], includePrivate?: boolean): {
//   pub1: string;
//   pub2: string;
//   pvt1: string;
//   pvt2: string;
// }[]

この状態では includePrivate を省略しても true を指定しても戻り値は User[] になります。

解決方法

引数の値からかっこよく推測する

https://www.asobou.co.jp/blog/web/typescript
こちらの記事では引数を参照してTypeScriptっぽくてかっこいいやり方が紹介されています。

もともとはこちらを参考に進めていたのですが、引数を省略可能にしたところでうまく効かなくなってしまい次の方法でやることになりました。

オーバーロード関数で引数のパターン分シグネチャを定義する

https://typescriptbook.jp/reference/functions/overload-functions

TypeScriptではひとつの関数に異なる関数シグネチャを複数持たせることができます。
この文法に従って includePrivate: true の場合とそれ以外の場合でシグネチャを定義することで戻り値の型を引数の値によって変えることが出来ます。

export function parse(
  data: Record<string, string>[],
  includePrivate?: boolean
): PublicUser[];
export function parse(
  data: Record<string, string>[],
  includePrivate: true
): User[];
export function parse(data: Record<string, string>[], includePrivate = false) {
  // ...
}

parse([{}]);
// function parse(data: Record<string, string>[], includePrivate?: boolean): PublicUser[] (+1 overload)

parse([{}], false);
// function parse(data: Record<string, string>[], includePrivate?: boolean): PublicUser[] (+1 overload)

parse([{}], true);
// function parse(data: Record<string, string>[], includePrivate: true): User[] (+1 overload)

少し(?)ブサイクですが、目的は達成できました。

まとめ

TypeScriptは難しい。

Discussion