🧧

TypeScript の Type Guard を使って、福袋の中身を判断してみたよ

2023/01/05に公開

はじめに

Type Guard(型ガード)っていつまで経っても良く分からんのです 😭
使用頻度も少なくて忘れちゃいそうなので、(今が旬の?)福袋を例に実装してみました。

実装したコードがこちら

福袋の中に「ロッテのパイの実が入っているのか確認したい!」を叶えるコードです。笑

function isObject<T extends Record<string, unknown>>(value: unknown): value is { [P in keyof T]?: unknown } {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}

function isString(value: unknown): value is string {
  return typeof value === 'string' && value !== null;
}

function isStringArray(value: unknown): value is string[] {
  return (
    typeof value === 'object' &&
    value !== null &&
    Array.isArray(value) &&
    !value.some((v) => !isString(v))
  );
}

type LuckyBag<T = unknown> = T;

function isLuckyBagInPainomi(
  luckyBag: LuckyBag,
): luckyBag is LuckyBag<{ lotte: { chocolates: ['painomi'] } }> {
  // NOTE: 以下のように、luckyBag をそのまま使うとエラーになります
  // console.log(luckyBag.lotte.chocolates)

  return (
    isObject<{ lotte: object }>(luckyBag) &&
    isObject<{ chocolates: string[] }>(luckyBag.lotte) &&
    isStringArray(luckyBag.lotte.chocolates) &&
    luckyBag.lotte.chocolates.includes('painomi')
  )
}

実行した結果がこちら

パイの実が入っていると true が返ってきます。安全ですね!

console.log(isLuckyBagInPainomi({
  lotte: { chocolates: ['painomi'] }
}))
// → true

console.log(isLuckyBagInPainomi({
  lotte: { chocolates: ['painomi', 'toppo'] }
}))
// → true

console.log(isLuckyBagInPainomi({
  lotte: { chocolates: ['painomi', 'toppo'] },
  nestle: { chocolates: ['kitkat'] }
}))
// → true

console.log(isLuckyBagInPainomi({
  lotte: { chocolates: ['toppo'] }
}))
// → false

console.log(isLuckyBagInPainomi({
  nestle: { chocolates: ['kitkat'] }
}))
// → false

console.log(isLuckyBagInPainomi(''))
// → false

最後に

TypeScriptPlayground にも書いてあるので、良かったら遊んでみてください 😊

Discussion