TEXTBOOK SECTION / AI LEARNING

型定義を作る

Next.jsによるWebアプリケーション開発概論の「Next.jsとTailwind CSSで作る 岐阜県ローカルエンタメニュースブログアプリ教材」より、型定義を作るを解説。生成AI、AI活用、DX、業務改善を実践しながら学べるオンライン教材です。

2Next.jsとTailwind CSSで作る 岐阜県ローカルエンタメニュースブログアプリ教材Next.js / Tailwind CSS / アプリ開発 / 開発 / Web開発

OVERVIEW

この節で学べること

概要を表示する
項目内容
教材名Next.jsによるWebアプリケーション開発概論
Next.jsとTailwind CSSで作る 岐阜県ローカルエンタメニュースブログアプリ教材
型定義を作る
カテゴリNext.js / Tailwind CSS / アプリ開発 / 開発 / Web開発
学習内容生成AI、AI活用、DX、業務改善を実践しながら理解するための教材です。

TABLE OF CONTENTS

目次

CONTENT

ここから

5. 型定義を作る

まず、ブログ記事の形を決めます。

types/blogs.ts を作成してください。

// types/blogs.ts

export type BlogCategory =
  | 'Entertainment'
  | 'Culture'
  | 'Music'
  | 'Design'
  | 'Tourism';

export type BlogPost = {
  id: string;
  slug: string;
  title: string;
  description: string;
  category: BlogCategory;
  tags: string[];
  publishedAt: string;
  readingMinutes: number;
  body: string[];
};

ここでやっていること

BlogPost は、記事1件分の設計図です。

title → 記事タイトル
description → 記事の短い説明
category → 記事カテゴリ
tags → タグ一覧
body → 本文

型を作っておくと、記事データの書き間違いに気づきやすくなります。

地味ですが、後でかなり助かります。


6. 記事データを作る

次に、data/posts.ts を作成します。コピーして活用してください。

// data/posts.ts

import type { BlogPost } from '@/types/blogs';

export const posts: BlogPost[] = [
  {
    id: 'post-001',
    slug: 'gifu-riverside-night-market',
    title: '長良川沿いに一夜限りのナイトマーケットが登場。音楽と屋台で2,000人が来場',
    description:
      '岐阜市の長良川河川敷で、地元アーティストと飲食店が集まるナイトマーケットが開催されました。',
    category: 'Entertainment',
    tags: ['岐阜市', '長良川', '音楽イベント', '屋台'],
    publishedAt: '2026-06-01',
    readingMinutes: 4,
    body: [
      '岐阜市の長良川河川敷で、地元の若手アーティストや飲食店が参加する一夜限りのナイトマーケット「Nagara River Night」が開催されました。',
      '会場には、アコースティックライブ、キッチンカー、手作り雑貨のブースが並び、夕方から夜にかけて約2,000人が来場。川風に揺れる照明と音楽が、普段の河川敷を少しだけ映画のワンシーンのように変えていました。',
      '主催者は、今後も季節ごとに開催を検討しているとのことです。地元の飲食店やクリエイターが自然に集まる場として、岐阜市の新しい夜の楽しみ方になりそうです。',
    ],
  },
  {
    id: 'post-002',
    slug: 'ogaki-retro-cinema-festival',
    title: '大垣の商店街でレトロ映画祭。空き店舗を使った小さな上映会が話題に',
    description:
      '大垣市の商店街で、昭和風の雰囲気を活かしたレトロ映画祭が開催されました。',
    category: 'Culture',
    tags: ['大垣市', '映画祭', '商店街', 'レトロ'],
    publishedAt: '2026-05-28',
    readingMinutes: 5,
    body: [
      '大垣市中心部の商店街で、空き店舗を活用した小規模な映画イベント「大垣レトロシネマ横丁」が開催されました。',
      '上映会場は、かつて洋品店だった店舗を改装したスペース。古い看板や木製の棚をそのまま残し、来場者がまるで昔の映画館に迷い込んだような空気を楽しめる演出が行われました。',
      'イベントでは、岐阜県内で活動する映像作家の短編作品も上映され、観客とのトークセッションも実施されました。商店街のにぎわいづくりとして、今後の継続開催に期待が集まっています。',
    ],
  },
  {
    id: 'post-003',
    slug: 'takayama-street-music-weekend',
    title: '高山の古い町並みにストリートミュージック。観光客と地元住民が足を止める週末に',
    description:
      '飛騨高山の古い町並みで、地元ミュージシャンによる週末限定の音楽イベントが行われました。',
    category: 'Music',
    tags: ['高山市', '飛騨高山', 'ストリートライブ', '観光'],
    publishedAt: '2026-05-20',
    readingMinutes: 6,
    body: [
      '高山市の古い町並み周辺で、地元ミュージシャンによる週末限定イベント「Takayama Street Music Weekend」が開催されました。',
      '三味線、ギター、ハンドパンなど、ジャンルの異なる演奏者が通り沿いに登場。観光客だけでなく、買い物帰りの地元住民も足を止め、普段より少しにぎやかな週末となりました。',
      '会場周辺のカフェや土産店では、音楽イベントに合わせた限定メニューも提供されました。観光地としての魅力に、地域の表現者が加わることで、新しい高山の楽しみ方が生まれています。',
    ],
  },
  {
    id: 'post-004',
    slug: 'seki-knife-fashion-show',
    title: '関市で“刃物とファッション”の異色ショー。職人技をランウェイで表現',
    description:
      '刃物の町として知られる関市で、伝統工芸とファッションを組み合わせたショーが開催されました。',
    category: 'Design',
    tags: ['関市', '刃物', 'ファッションショー', '伝統工芸'],
    publishedAt: '2026-05-10',
    readingMinutes: 3,
    body: [
      '関市で、刃物職人の技術とファッションを組み合わせたイベント「SEKI EDGE COLLECTION」が開催されました。',
      '会場では、包丁やはさみそのものを見せるのではなく、金属の輝き、刃の曲線、職人の手仕事から着想を得た衣装が披露されました。少し尖った企画ですが、そこが妙に関市らしい。静かな熱量がありました。',
      '地元の高校生や若手デザイナーも制作に参加し、伝統産業を次の世代へどう伝えるかを考える場にもなりました。工芸とエンタメの接点として、今後の展開が注目されます。',
    ],
  },
];

/**
 * 役割: 公開日が新しい順に記事一覧を取得する
 * 入力: なし
 * 出力: 公開日の降順に並んだブログ記事配列
 */
export function getSortedPosts(): BlogPost[] {
  return [...posts].sort((firstPost, secondPost) => {
    return (
      new Date(secondPost.publishedAt).getTime() -
      new Date(firstPost.publishedAt).getTime()
    );
  });
}

/**
 * 役割: slugに一致する記事を取得する
 * 入力: 記事slug
 * 出力: 一致したブログ記事、存在しない場合はundefined
 */
export function getPostBySlug(slug: string): BlogPost | undefined {
  return posts.find((post) => post.slug === slug);
}

/**
 * 役割: 検索キーワードに一致する記事一覧を取得する
 * 入力: 検索キーワード
 * 出力: タイトル・説明文・タグに一致したブログ記事配列
 */
export function searchPosts(keyword: string): BlogPost[] {
  const normalizedKeyword = keyword.trim().toLowerCase();

  if (normalizedKeyword.length === 0) {
    return getSortedPosts();
  }

  return getSortedPosts().filter((post) => {
    const searchableText = [
      post.title,
      post.description,
      post.category,
      ...post.tags,
    ]
      .join(' ')
      .toLowerCase();

    return searchableText.includes(normalizedKeyword);
  });
}

ここで大事な考え方

記事をただ並べるだけではなく、3つの関数を作っています。

関数役割
getSortedPosts新しい順に記事を並べる
getPostBySlugURL用のslugから記事を探す
searchPosts検索キーワードで記事を絞り込む

この3つがあると、トップページ、詳細ページ、検索機能が作りやすくなります。


FAQ

よくある質問

型定義を作るは医療関係者向けだけの内容ですか。
医療分野の例が含まれる場合もありますが、医療関係者だけに限定した内容ではありません。生成AI、AI活用、DX、業務改善、プロトタイプ開発など、一般的なAI学習の事例として読める内容です。
AI初心者でも読めますか。
はい。AIをこれから学ぶ方、数学が苦手な方、仕事でAIを使いたい方にも読み進めやすいように、教材の章と節の流れに沿って整理しています。
サムネイル画像は必ず表示されますか。
はい。教材にcoverUrlが設定されている場合はその画像を表示し、未設定の場合は代替サムネイル画像を表示します。
Next.jsによるWebアプリケーション開発概論のほかの章も読めますか。
はい。教材トップから章立てを確認でき、前後の節へもページ下部のナビゲーションから移動できます。