`f(data)` 形式だとオプショナルチェーン（`data?.method()`）が無いので、ユーティリティを用意しよう

オプショナルチェーンって、便利ですよね？

```tsx
publishedAt?.toDateString();
```

「publishedAt を `toDateString()` で文字列に変換する。ただし、publishedAt が nullable（undefined / null の可能性あり）である。」

のように、`data.method()` 形式の、変換するタイプの処理で変換元が nullable のときに、コードを短く簡潔にできるので便利ですよね？

ただ、JavaScript には、 `f(data)` を同様にシンプルにするような機能がありません。

---

以下のコードを見てみましょう。

```tsx
import type { FC } from "react";

import { pipe } from "remeda";

type Props = {
  slug: string;
  title: string;
  publishedAt: Date | undefined;
};

const dateTimeFormat = new Intl.DateTimeFormat("ja-JP", {
  timeZone: "UTC",
  dateStyle: "full",
  timeStyle: "short",
});

const formatPublishedAt = (d: Date) => `${dateTimeFormat.format(d)}（UTC）`;

export const PostListItem: FC<Props> = ({ title, publishedAt }) => {
  return (
    <article css={{ padding: "1rem", display: "grid", gap: "0.5rem" }}>
      <h3>{title}</h3>
      {pipe(publishedAt, (d) => {
        if (d == null) return <div>投稿日時の指定なし</div>;
        return <div>{`${formatPublishedAt(d)}`}</div>;
      })}
    </article>
  );
};

```

`Intl.DateTimeFormat` を使った `formatPublishedAt(date)` 関数をコンポーネントの外側に抽出して、この関数をコンポーネント内で呼び出して、指定がない時にはフォールバック表示をしています。

---

個人的な好みとして、JavaScript には式指向的機能が乏しいので、それ補うために [Remeda というライブラリの `pipe` 関数](https://remedajs.com/docs/#pipe) を使用しています。

```tsx
import { pipe } from "remeda";

pipe(data, f, g, h) // === h(g(f(data)))
```

基本的には、この `pipe` 関数によって《コードの中を上から下に向かってデータが流れる》という流れを作ります。

**「`data` について、`f`, `g`, `h` という変換をこの順に施す」** のように、主題優勢言語っぽく、（OOUI 的な意味での）オブジェクト指向的で、個人的に好きです。

---

null | undefined を扱うときにも、同様の《流れ》を生み出せるようにしようと思って、こんなユーティリティを作ってみました。

```tsx
/**
 * null | undefined についてのガードを行わない関数を受け取って、
 * 
 * null | undefined のときに undefined を返すガード付きの関数に変換する。
 */
export const safeNullable =
  <A, B>(fn: (a: A) => B) =>
  (value: A | undefined | null): B | undefined => {
    if (value == null) {
      return undefined;
    }
    return fn(value);
  };
```

これと pipe を組み合わせると、

- `publishedAt` について、
- これを `formatPublishedAt` で変換する
    - null | undefined のときは変換せずスルー
- 最終的に div に挟む、
    - null | undefined のときはフォールバック表示

のような流れが出来ますね？


```tsx
import type { FC } from "react";

import { pipe } from "remeda";
import { safeNullable } from "../_utils/safe-nullable";

type Props = {
  slug: string;
  title: string;
  publishedAt: Date | undefined;
};

const dateTimeFormat = new Intl.DateTimeFormat("ja-JP", {
  timeZone: "UTC",
  dateStyle: "full",
  timeStyle: "short",
});

const formatPublishedAt = (d: Date) => `${dateTimeFormat.format(d)}（UTC）`;

export const PostListItem: FC<Props> = ({ title, publishedAt }) => {
  return (
    <article css={{ padding: "1rem", display: "grid", gap: "0.5rem" }}>
      <h3>{title}</h3>
      {pipe(
        publishedAt, // publishedAt を対象に取って、
        safeNullable(formatPublishedAt), // これに、 formatPublishedAt を通す (nullish なときはスルー)
        // そして、それを div で包む、 nullish なときはフォールバック表示 
        (d) => (d != null ? <div>{d}</div> : <div>投稿日時の指定なし</div>)
      )}
    </article>
  );
};

```

## まとめ

注意点があります。

`safeNullable` を使うよりも、書き換え前の「一つのif文を使う」ほうがロジックを素直に書ける場合もあります。

実は、今回のようなケースは、「date を div として出力する」という一つの関数またはコンポーネントを作ってしまったほうが理想的です。

なので、「すべての場合について、こっちのほうがキレイに書ける」という正解があると考えて思考停止するのではなく、**該当するロジックと真剣に向き合って、ベターな書き方を検討しましょう。**

この記事が、その「ロジックを素直に書ける方法」の選択肢を増やす一助になることを願います。

## 関連記事

https://qiita.com/honey32/items/2abe8ca1443b57824396