🧮

【React × TypeScript】医療系アプリで学ぶ基準値UI設計:③ 型安全に基準値を扱う!比較ロジックを安全設計する方法

に公開

📘 はじめに

Next.js + TypeScript + Tailwind CSSを学習しながら、
看護現場で役に立ちそうな「看護師向け計算ツールアプリ」の開発を進めております。

このアプリでは、計算結果を基準値(正常範囲)と照らし合わせて
「高い」「低い」を自動で判定します👇

🔺135より高ければ赤矢印
🔻135より低ければ青矢印

しかし初期実装では、こんな問題が発生しました。

  • 数値が文字列のまま比較される
  • nullundefinedNaN が出る
  • 基準値の型が合わずにバグが出る

この記事では、これらをTypeScriptで型安全に扱う方法を解説します。

💡 比較の落とし穴

たとえば単純に以下のように書いていたとします。

if (value > range.max) return "高い";

ここで value が "140"(文字列)だった場合……


"9" > "100"; // true 😱(文字列比較)

JavaScriptでは暗黙の型変換で誤った比較が起きることがあります。
医療アプリのように「正確な数値」が前提の場面では致命的です。

⚙️ 型安全な比較ロジックを設計する

① Range型(基準値)


export type Range = {
  min: number;
  max: number;
};

② 比較関数


export const compareValueToRange = (value: unknown, range?: Range) => {
  if (range === undefined) return "N/A";

  // 1. 数値変換
  const numericValue = parseFloat(String(value));

  // 2. 数値でない場合は安全に終了
  if (isNaN(numericValue)) return "invalid";

  // 3. 比較処理
  if (numericValue > range.max) return "high";
  if (numericValue < range.min) return "low";
  return "normal";
};

✅ ポイント

工夫 内容
unknown 外部入力値(文字列や空値)を安全に受け取れる
parseFloat(String(value)) すべて文字列化して数値変換
isNaN() チェック 不正値を除外
range? オプショナル型で安全に比較

🧠 型ガードでさらに安全に

もし value が文字列・数値・nullなど混在するケースが多い場合、
型ガード関数を使うとさらに堅牢になります。


export const isNumeric = (value: unknown): value is number => {
  return typeof value === "number" && !isNaN(value);
};

そして比較時に使う:


if (!isNumeric(value)) {
  console.warn("数値ではありません:", value);
  return "invalid";
}

📊 実用例:ResultBox で使う

import { compareValueToRange } from "@/lib/compareValueToRange";
import { getStatusIcon } from "@/lib/getStatusIcon";
import { normalRanges } from "@/config/normalRanges";

const range = normalRanges.potassium;
const resultType = compareValueToRange(value, range);

return (
  <div className="flex items-center gap-2">
    <span className="text-lg">{value}</span>
    {getStatusIcon(value, range)}
    {resultType === "invalid" && (
      <span className="text-gray-400 text-sm">(値が不明です)</span>
    )}
  </div>
);

🧪 テスト例(Vitest / Jest)

test("compareValueToRange works correctly", () => {
  const range = { min: 3.5, max: 5.0 };
  expect(compareValueToRange(3.0, range)).toBe("low");
  expect(compareValueToRange(4.0, range)).toBe("normal");
  expect(compareValueToRange(6.0, range)).toBe("high");
  expect(compareValueToRange("abc", range)).toBe("invalid");
});

テストを追加することで、バリデーションの信頼性が高まります。

🩺 医療UIの安全設計ポイント

観点 工夫
想定外入力 空欄・文字列・nullを安全に無視
型安全性 unknown → number変換で堅牢化
戻り値 `"high"
再利用 ResultBox/履歴判定など複数箇所で共通利用

🧠 まとめ

項目 内容
⚙️ 比較関数 compareValueToRange() で安全に比較
🧱 型ガード isNumeric() で数値検証
🩺 安全設計 不正値を安全にスルー
🧩 ライブラリ判断 小規模なら型ガード、大規模入力ならZod系
🧪 信頼性 テスト追加で安全性を担保

🔗 シリーズ案内

🩺 【React × TypeScript】医療系アプリで学ぶ基準値UI設計シリーズ

1.基準値より高い・低いを矢印で表示する方法
2.normalRanges.tsで基準値を一元管理する設計パターン
3.型安全に基準値を扱う!比較ロジックを安全設計する方法 ←(この記事
4.見やすさ重視のUI設計!Tailwindで基準値判定をわかりやすく表示する

Discussion