{"id":13197,"date":"2026-03-17T06:28:29","date_gmt":"2026-03-17T06:28:29","guid":{"rendered":"https:\/\/withcode.tech\/media\/?p=13197"},"modified":"2026-07-01T15:28:28","modified_gmt":"2026-07-01T15:28:28","slug":"typescript-literal-types-guide","status":"publish","type":"post","link":"https:\/\/withcode.tech\/media\/typescript-literal-types-guide\/","title":{"rendered":"TypeScript\u306e\u30ea\u30c6\u30e9\u30eb\u578b\u5b8c\u5168\u30ac\u30a4\u30c9\uff5c\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30ea\u30c6\u30e9\u30eb\u578b\u30fbconst\u30a2\u30b5\u30fc\u30b7\u30e7\u30f3\u30fb\u578b\u5b89\u5168CSS-in-JS\u30fbTailwind CVA\u30fbvanilla-extract\u30fb\u30c7\u30b6\u30a4\u30f3\u30c8\u30fc\u30af\u30f3\u5b9f\u88c5\u307e\u3067\u5fb9\u5e95\u89e3\u8aac"},"content":{"rendered":"\n<div class=\"wp-block-group swl-box swl-box--border\"><div class=\"wp-block-group__inner-container\">\n\n<h3 class=\"wp-block-heading\">\u3053\u306e\u8a18\u4e8b\u3067\u308f\u304b\u308b\u3053\u3068<\/h3>\n\n\n<ul class=\"wp-block-list\">\n<li>\u30ea\u30c6\u30e9\u30eb\u578b\u306e\u57fa\u790e\uff08\u6587\u5b57\u5217\u30fb\u6570\u5024\u30fb\u771f\u507d\u5024\uff09\u3068\u30bf\u30a4\u30dd\u3092\u30b3\u30f3\u30d1\u30a4\u30eb\u6642\u306b\u691c\u51fa\u3067\u304d\u308b\u4ed5\u7d44\u307f<\/li>\n<li>\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30ea\u30c6\u30e9\u30eb\u578b\u3067CSS\u30af\u30e9\u30b9\u540d\u3092\u52d5\u7684\u306b\u69cb\u7bc9\u3059\u308b\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3<\/li>\n<li>const\u30a2\u30b5\u30fc\u30b7\u30e7\u30f3\u30fbEnum\u3068\u306e\u6bd4\u8f03\u30fb\u578bnarrowing\u3068\u30e6\u30fc\u30b6\u30fc\u5b9a\u7fa9\u578b\u30ac\u30fc\u30c9\u306e\u4f7f\u3044\u65b9<\/li>\n<li>Tailwind CSS \u00d7 CVA\uff08class-variance-authority\uff09\u3067\u578b\u5b89\u5168\u306a\u30d0\u30ea\u30a2\u30f3\u30c8\u7ba1\u7406\u3092\u5b9f\u73fe\u3059\u308b\u65b9\u6cd5<\/li>\n<li>\u30c7\u30b6\u30a4\u30f3\u30c8\u30fc\u30af\u30f3\u30fbvanilla-extract\u30fb\u30d6\u30e9\u30f3\u30c9\u578b\uff08Branded Types\uff09\u306e\u5b9f\u8df5\u7684\u6d3b\u7528\u6cd5<\/li>\n<\/ul>\n\n<\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u7d50\u8ad6\u304b\u3089\u8a00\u3046\u3068\u3001TypeScript\u306e\u30ea\u30c6\u30e9\u30eb\u578b\u3067CSS\u30af\u30e9\u30b9\u540d\u3084\u30c7\u30b6\u30a4\u30f3\u30c8\u30fc\u30af\u30f3\u3092\u578b\u4ed8\u3051\u3059\u308b\u3053\u3068\u3067\u3001\u30b9\u30bf\u30a4\u30eb\u306e\u30bf\u30a4\u30dd\u30fb\u5b58\u5728\u3057\u306a\u3044\u30d0\u30ea\u30a2\u30f3\u30c8\u306e\u6307\u5b9a\u30fb\u30c7\u30b6\u30a4\u30f3\u30c8\u30fc\u30af\u30f3\u306e\u8aa4\u7528\u3092\u30b3\u30f3\u30d1\u30a4\u30eb\u6642\u306b\u9632\u3052\u307e\u3059\u3002<\/strong>\u6587\u5b57\u5217\u3067CSS\u30af\u30e9\u30b9\u3092\u6e21\u3059\u5834\u5408\u3001\u30bf\u30a4\u30dd\u306f\u5b9f\u884c\u6642\u307e\u3067\u767a\u898b\u3067\u304d\u307e\u305b\u3093\u3002\u30ea\u30c6\u30e9\u30eb\u578b\u3092\u4f7f\u3048\u3070IDE\u306e\u88dc\u5b8c\u3082\u5229\u304d\u3001\u5909\u66f4\u306b\u5f37\u3044\u30b3\u30fc\u30c9\u304c\u5b9f\u73fe\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u30ea\u30c6\u30e9\u30eb\u578b\u3068\u306f\uff5c\u7279\u5b9a\u306e\u5024\u306e\u307f\u3092\u8a31\u53ef\u3059\u308b\u578b<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ \u901a\u5e38\u306e\u578b\uff08string\uff09\uff1a\u3069\u3093\u306a\u6587\u5b57\u5217\u3067\u3082\u53d7\u3051\u4ed8\u3051\u308b\nlet direction: string = 'top'\ndirection = 'anything'  \/\/ \u2705 \u30a8\u30e9\u30fc\u306b\u306a\u3089\u306a\u3044\uff08\u30bf\u30a4\u30dd\u3082\u6c17\u3065\u304b\u306a\u3044\uff09\n\n\/\/ \u6587\u5b57\u5217\u30ea\u30c6\u30e9\u30eb\u578b\uff1a\u7279\u5b9a\u306e\u6587\u5b57\u5217\u306e\u307f\u53d7\u3051\u4ed8\u3051\u308b\ntype Direction = 'top' | 'right' | 'bottom' | 'left'\n\nlet dir: Direction\ndir = 'top'     \/\/ \u2705 OK\ndir = 'center'  \/\/ \u274c \u30a8\u30e9\u30fc: Type '\"center\"' is not assignable to type 'Direction'\ndir = 'tops'    \/\/ \u274c \u30a8\u30e9\u30fc: \u30bf\u30a4\u30dd\u3092\u5373\u5ea7\u306b\u691c\u51fa\uff01\n\n\/\/ \u6570\u5024\u30ea\u30c6\u30e9\u30eb\u578b\uff1a\u7279\u5b9a\u306e\u6570\u5024\u306e\u307f\u53d7\u3051\u4ed8\u3051\u308b\ntype FontSize = 12 | 14 | 16 | 18 | 24 | 32 | 48 | 64\nlet size: FontSize\nsize = 16  \/\/ \u2705 OK\nsize = 15  \/\/ \u274c \u30a8\u30e9\u30fc: Type '15' is not assignable to type 'FontSize'\n\n\/\/ \u30ea\u30c6\u30e9\u30eb\u578b\u306e\u6069\u6075\n\/\/ 1. IDE\u306e\u30aa\u30fc\u30c8\u30b3\u30f3\u30d7\u30ea\u30fc\u30c8\u3067\u9078\u629e\u80a2\u3092\u8868\u793a\n\/\/ 2. \u30bf\u30a4\u30dd\u3092\u30b3\u30f3\u30d1\u30a4\u30eb\u6642\u306b\u691c\u51fa\n\/\/ 3. \u7db2\u7f85\u6027\u30c1\u30a7\u30c3\u30af\uff08Record\u3084switch\u6587\u3067\u6f0f\u308c\u3092\u691c\u51fa\uff09\n\/\/ 4. \u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u52b9\u679c\uff08\u578b\u540d\u3092\u898b\u308b\u3068\u4f55\u3092\u6e21\u305b\u308b\u304b\u4e00\u76ee\u77ad\u7136\uff09<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30ea\u30c6\u30e9\u30eb\u578b\uff5c\u6587\u5b57\u5217\u306e\u7d44\u307f\u5408\u308f\u305b\u3092\u578b\u3067\u8868\u73fe<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">TypeScript 4.1\u3067\u5c0e\u5165\u3055\u308c\u305f<strong>\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30ea\u30c6\u30e9\u30eb\u578b<\/strong>\u306f\u3001\u6587\u5b57\u5217\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u578b\u3068\u3057\u3066\u4f7f\u3048\u308b\u3088\u3046\u306b\u3057\u305f\u6a5f\u80fd\u3067\u3059\u3002CSS\u306e\u30af\u30e9\u30b9\u540d\u751f\u6210\u3084\u72b6\u614b\u540d\u306e\u5b9a\u7fa9\u306b\u7279\u306b\u6709\u52b9\u3067\u3059\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30ea\u30c6\u30e9\u30eb\u578b\u306e\u57fa\u672c\ntype Color = 'red' | 'green' | 'blue'\ntype Size = 'sm' | 'md' | 'lg'\n\n\/\/ 2\u3064\u306e\u578b\u3092\u7d44\u307f\u5408\u308f\u305b\u3066\u5168\u7d44\u307f\u5408\u308f\u305b\u3092\u81ea\u52d5\u751f\u6210\ntype ColorSize = `${Color}-${Size}`\n\/\/ \"red-sm\" | \"red-md\" | \"red-lg\" | \"green-sm\" | \"green-md\" | ... (9\u901a\u308a)\n\ntype BgClass = `bg-${Color}`\n\/\/ \"bg-red\" | \"bg-green\" | \"bg-blue\"\n\n\/\/ Tailwind\u306e\u30ab\u30e9\u30fc\u30b9\u30b1\u30fc\u30eb\u3092\u578b\u3067\u8868\u73fe\ntype TailwindColor = 'slate' | 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'violet' | 'pink'\ntype TailwindScale = 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900\ntype TailwindBg = `bg-${TailwindColor}-${TailwindScale}`\n\/\/ \"bg-slate-100\" | \"bg-slate-200\" | ... | \"bg-pink-900\"\uff0872\u901a\u308a\uff09\n\nfunction setBackground(className: TailwindBg) {\n  document.body.className = className\n}\n\nsetBackground('bg-blue-500')   \/\/ \u2705 OK\nsetBackground('bg-blue-250')   \/\/ \u274c \u30a8\u30e9\u30fc: \u5b58\u5728\u3057\u306a\u3044\u30b9\u30b1\u30fc\u30eb\nsetBackground('bg-purple-500') \/\/ \u274c \u30a8\u30e9\u30fc: \u5b9a\u7fa9\u3055\u308c\u3066\u3044\u306a\u3044\u8272\n\n\/\/ Capitalize \/ Lowercase \/ Uppercase \/ Uncapitalize\uff08\u7d44\u307f\u8fbc\u307f\u5909\u63db\u578b\uff09\ntype EventName = 'click' | 'focus' | 'blur'\ntype HandlerName = `on${Capitalize<EventName>}`  \/\/ \"onClick\" | \"onFocus\" | \"onBlur\"\n\ntype CSSProperty = 'fontSize' | 'backgroundColor' | 'borderRadius'\ntype CSSVariable = `--${Lowercase<CSSProperty>}`\n\/\/ \"--fontsize\" | \"--backgroundcolor\" | \"--borderradius\"<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u5b9f\u8df5\u4f8b\uff1a\u30ab\u30c6\u30b4\u30ea\u30fc\u5225\u30c6\u30fc\u30de\u30ab\u30e9\u30fc\u306e\u578b\u5b89\u5168\u5b9f\u88c5<\/h2>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-full\"><img decoding=\"async\" src=\"img\/typescript_literal_type_css.png\" alt=\"TypeScript\u30ea\u30c6\u30e9\u30eb\u578b\u3092\u4f7f\u3063\u305f\u30ab\u30c6\u30b4\u30ea\u30fc\u5225\u30c6\u30fc\u30de\u30ab\u30e9\u30fc\u306e\u5b9f\u88c5\u4f8b\" class=\"wp-image-12580\"\/><\/figure>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ types\/category.ts\n\n\/\/ \u30ab\u30c6\u30b4\u30ea\u30fc\u3092\u30ea\u30c6\u30e9\u30eb\u578b\u3067\u5b9a\u7fa9\uff08\u6587\u5b57\u5217\u306e\u5236\u7d04\uff09\nexport type Category = 'technology' | 'health' | 'finance' | 'lifestyle' | 'travel'\n\nexport type Article = {\n  id: number\n  title: string\n  content: string\n  category: Category  \/\/ \u2190 5\u3064\u306e\u5024\u3057\u304b\u53d7\u3051\u4ed8\u3051\u306a\u3044\n  publishedAt: string\n}\n\n\/\/ Category\u3092\u8ffd\u52a0\u3057\u305f\u5834\u5408\uff1a\n\/\/ \u2192 Record<Category, string> \u5074\u306b\u30a8\u30e9\u30fc\u304c\u51fa\u3066\u8ffd\u52a0\u6f0f\u308c\u3092\u9632\u3052\u308b\uff01<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ utils\/category\/color.ts\n\nimport type { Category } from '@\/types\/category'\n\n\/\/ \u30ab\u30c6\u30b4\u30ea\u30fc \u2192 Tailwind CSS\u30af\u30e9\u30b9\u3078\u306e\u30de\u30c3\u30d4\u30f3\u30b0\n\/\/ Record<Category, string> \u3067\u5168\u30ab\u30c6\u30b4\u30ea\u30fc\u306e\u30de\u30c3\u30d4\u30f3\u30b0\u304c\u5fc5\u9808\u306b\u306a\u308b\nconst CATEGORY_CONFIG: Record<Category, {\n  bg: string\n  text: string\n  border: string\n  label: string\n}> = {\n  technology: {\n    bg: 'bg-green-100',\n    text: 'text-green-800',\n    border: 'border-green-300',\n    label: '\u30c6\u30af\u30ce\u30ed\u30b8\u30fc',\n  },\n  health: {\n    bg: 'bg-amber-100',\n    text: 'text-amber-800',\n    border: 'border-amber-300',\n    label: '\u30d8\u30eb\u30b9',\n  },\n  finance: {\n    bg: 'bg-cyan-100',\n    text: 'text-cyan-800',\n    border: 'border-cyan-300',\n    label: '\u30d5\u30a1\u30a4\u30ca\u30f3\u30b9',\n  },\n  lifestyle: {\n    bg: 'bg-yellow-100',\n    text: 'text-yellow-800',\n    border: 'border-yellow-300',\n    label: '\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb',\n  },\n  travel: {\n    bg: 'bg-pink-100',\n    text: 'text-pink-800',\n    border: 'border-pink-300',\n    label: '\u65c5\u884c',\n  },\n}\n\n\/\/ \u578b\u5b89\u5168\u306a\u30c6\u30fc\u30de\u53d6\u5f97\u95a2\u6570\nexport function getCategoryConfig(category: Category) {\n  return CATEGORY_CONFIG[category]\n  \/\/ \u5b58\u5728\u3057\u306a\u3044\u30ab\u30c6\u30b4\u30ea\u30fc\u3092\u6e21\u3059\u3068\u30b3\u30f3\u30d1\u30a4\u30eb\u30a8\u30e9\u30fc\n  \/\/ \u30de\u30c3\u30d4\u30f3\u30b0\u306b\u6f0f\u308c\u304c\u3042\u308c\u3070TypeScript\u304c\u8b66\u544a\n}\n\n\/\/ \u30ab\u30c6\u30b4\u30ea\u30fc\u30d0\u30c3\u30b8\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\ninterface CategoryBadgeProps {\n  category: Category\n  size?: 'sm' | 'md' | 'lg'\n}\n\nexport function CategoryBadge({ category, size = 'md' }: CategoryBadgeProps) {\n  const config = getCategoryConfig(category)\n  const sizeClass = { sm: 'text-xs px-2 py-0.5', md: 'text-sm px-2.5 py-1', lg: 'text-base px-3 py-1.5' }[size]\n\n  return (\n    <span className={`\n      ${config.bg} ${config.text} ${config.border}\n      ${sizeClass}\n      border rounded-full font-medium\n    `}>\n      {config.label}\n    <\/span>\n  )\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">const\u30a2\u30b5\u30fc\u30b7\u30e7\u30f3\uff08as const\uff09\u3067\u3088\u308a\u5805\u7262\u306a\u578b\u5b9a\u7fa9<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ as const \u3067\u5b9a\u6570\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u30ea\u30c6\u30e9\u30eb\u578b\u306b\u5909\u63db\u3059\u308b\n\n\/\/ as const \u306a\u3057\uff08\u4e00\u822c\u7684\u306a\u578b\u306b\u306a\u308b\uff09\nconst COLORS = {\n  primary: '#3B82F6',   \/\/ \u2192 \u578b: string\n  success: '#10B981',   \/\/ \u2192 \u578b: string\n}\n\n\/\/ as const \u3042\u308a\uff08\u30ea\u30c6\u30e9\u30eb\u578b\u306b\u306a\u308b\uff09\nconst COLORS = {\n  primary: '#3B82F6',   \/\/ \u2192 \u578b: \"#3B82F6\"\uff08\u5b8c\u5168\u306a\u30ea\u30c6\u30e9\u30eb\u578b\uff09\n  secondary: '#6B7280', \/\/ \u2192 \u578b: \"#6B7280\"\n  success: '#10B981',   \/\/ \u2192 \u578b: \"#10B981\"\n  danger: '#EF4444',    \/\/ \u2192 \u578b: \"#EF4444\"\n  warning: '#F59E0B',   \/\/ \u2192 \u578b: \"#F59E0B\"\n} as const\n\n\/\/ \u30ad\u30fc\u306e\u578b\u3092\u62bd\u51fa\ntype ColorKey = keyof typeof COLORS\n\/\/ \u2192 \"primary\" | \"secondary\" | \"success\" | \"danger\" | \"warning\"\n\n\/\/ \u5024\u306e\u578b\u3092\u62bd\u51fa\ntype ColorValue = typeof COLORS[ColorKey]\n\/\/ \u2192 \"#3B82F6\" | \"#6B7280\" | \"#10B981\" | \"#EF4444\" | \"#F59E0B\"\n\n\/\/ \u578b\u5b89\u5168\u306a\u30ab\u30e9\u30fc\u53c2\u7167\u95a2\u6570\nfunction getColor(key: ColorKey): ColorValue {\n  return COLORS[key]\n  \/\/ \u5b58\u5728\u3057\u306a\u3044\u30ad\u30fc\u3092\u6e21\u3059\u3068\u30b3\u30f3\u30d1\u30a4\u30eb\u30a8\u30e9\u30fc\n}\n\n\/\/ \u914d\u5217\u3067\u3082\u4f7f\u3048\u308b\nconst BREAKPOINTS = [480, 768, 1024, 1280, 1536] as const\ntype Breakpoint = typeof BREAKPOINTS[number]  \/\/ 480 | 768 | 1024 | 1280 | 1536\n\n\/\/ \u30cd\u30b9\u30c8\u3057\u305f\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\nconst THEME = {\n  colors: {\n    primary: { 100: '#EBF5FB', 500: '#3B82F6', 900: '#1E3A8A' },\n    gray: { 100: '#F3F4F6', 500: '#6B7280', 900: '#111827' },\n  },\n  fontSizes: { xs: '0.75rem', sm: '0.875rem', base: '1rem', lg: '1.125rem' },\n} as const\n\ntype ThemeColors = typeof THEME['colors']\ntype PrimaryShade = keyof typeof THEME['colors']['primary']  \/\/ 100 | 500 | 900<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Enum\u3068\u30ea\u30c6\u30e9\u30eb\u578b\u306e\u6bd4\u8f03\uff5c\u3069\u3061\u3089\u3092\u4f7f\u3046\u3079\u304d\u304b<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>\u6bd4\u8f03\u9805\u76ee<\/th><th>\u6587\u5b57\u5217\u30ea\u30c6\u30e9\u30eb\u578b\uff08Union\uff09<\/th><th>TypeScript Enum<\/th><th>const Object\uff08Enum\u4ee3\u66ff\uff09<\/th><\/tr><\/thead><tbody><tr><td>\u578b\u306e\u5024<\/td><td>\u305d\u306e\u307e\u307e\u4f7f\u3048\u308b\uff08&#8217;active&#8217;\uff09<\/td><td>\u5217\u6319\u578b\u5c02\u7528\uff08Status.Active\uff09<\/td><td>\u305d\u306e\u307e\u307e\u4f7f\u3048\u308b\uff08Status.Active = &#8216;active&#8217;\uff09<\/td><\/tr><tr><td>IDE\u30b5\u30dd\u30fc\u30c8<\/td><td>\u25ce<\/td><td>\u25ce<\/td><td>\u25ce<\/td><\/tr><tr><td>\u30d0\u30f3\u30c9\u30eb\u30b5\u30a4\u30ba<\/td><td>\u25ce\uff08\u578b\u306f\u6d88\u3048\u308b\uff09<\/td><td>\u25b3\uff08\u30e9\u30f3\u30bf\u30a4\u30e0\u30b3\u30fc\u30c9\u304c\u751f\u6210\u3055\u308c\u308b\uff09<\/td><td>\u25ce\uff08const \u306f\u6700\u9069\u5316\u53ef\u80fd\uff09<\/td><\/tr><tr><td>JSON\u4e92\u63db<\/td><td>\u25ce\uff08\u305d\u306e\u307e\u307e\uff09<\/td><td>\u25b3\uff08\u6570\u5024Enum\u306f\u6ce8\u610f\uff09<\/td><td>\u25ce<\/td><\/tr><tr><td>\u5b9a\u6570\u540d\u306e\u610f\u5473<\/td><td>\u25b3\uff08\u5b9a\u6570\u540d\u306a\u3057\uff09<\/td><td>\u25cb<\/td><td>\u25ce\uff08SomeEnum.VALUE \u3067\u660e\u78ba\uff09<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ \u2705 \u63a8\u5968\uff1aconst Object + Union\u578b\uff08Enum\u4ee3\u66ff\u30d1\u30bf\u30fc\u30f3\uff09\nconst Status = {\n  Active: 'active',\n  Inactive: 'inactive',\n  Pending: 'pending',\n} as const\ntype Status = (typeof Status)[keyof typeof Status]\n\/\/ type Status = \"active\" | \"inactive\" | \"pending\"\n\n\/\/ \u4f7f\u3044\u65b9\uff08Enum\u540c\u69d8\u306b\u610f\u56f3\u304c\u660e\u78ba\uff09\nfunction updateStatus(status: Status) {\n  console.log(status)\n}\nupdateStatus(Status.Active)  \/\/ 'active'\nupdateStatus('active')       \/\/ \u2190 \u3053\u308c\u3082\u53d7\u3051\u4ed8\u3051\u308b\uff08Union\u578b\u306a\u306e\u3067\uff09\n\n\/\/ \u3069\u3061\u3089\u3082\u53d7\u3051\u4ed8\u3051\u308b\u305f\u3081\u3001\u30a2\u30d7\u30ea\u30b3\u30fc\u30c9\u5185\u3067\u306f Status.Active \u3092\u4f7f\u3046\u898f\u7d04\u306b\u3059\u308b\u3053\u3068\u3092\u63a8\u5968<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u578bnarrowing\u3068\u30e6\u30fc\u30b6\u30fc\u5b9a\u7fa9\u578b\u30ac\u30fc\u30c9<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ \u578bnarrowing\uff1a\u6761\u4ef6\u306b\u3088\u3063\u3066\u578b\u3092\u7d5e\u308a\u8fbc\u3080\ntype Shape =\n  | { kind: 'circle'; radius: number }\n  | { kind: 'rectangle'; width: number; height: number }\n  | { kind: 'triangle'; base: number; height: number }\n\nfunction getArea(shape: Shape): number {\n  \/\/ switch\u6587\u306ekind\u30d7\u30ed\u30d1\u30c6\u30a3\uff08discriminated union\uff09\u3067narrowing\u3059\u308b\n  switch (shape.kind) {\n    case 'circle':\n      return Math.PI * shape.radius ** 2  \/\/ \u3053\u3053\u3067\u306fshape.radius\u304c\u78ba\u5b9f\u306b\u5b58\u5728\n    case 'rectangle':\n      return shape.width * shape.height   \/\/ \u3053\u3053\u3067\u306fshape.width\u304c\u78ba\u5b9f\u306b\u5b58\u5728\n    case 'triangle':\n      return (shape.base * shape.height) \/ 2\n    default:\n      \/\/ TypeScript\u304c\u5168\u30b1\u30fc\u30b9\u3092\u30ab\u30d0\u30fc\u3057\u3066\u3044\u308b\u304b\u691c\u8a3c\u3059\u308b\u6280\u6cd5\n      const exhaustiveCheck: never = shape\n      throw new Error(`Unknown shape: ${exhaustiveCheck}`)\n  }\n}\n\n\/\/ CSS \u30d0\u30ea\u30a2\u30f3\u30c8\u3067\u306e discriminated union \u6d3b\u7528\ntype ButtonVariant =\n  | { variant: 'primary'; loading?: boolean }\n  | { variant: 'secondary' }\n  | { variant: 'danger'; confirmText?: string }\n  | { variant: 'ghost' }\n\nfunction getButtonClass(props: ButtonVariant): string {\n  switch (props.variant) {\n    case 'primary':\n      return props.loading ? 'btn-primary opacity-50 cursor-wait' : 'btn-primary'\n    case 'secondary':\n      return 'btn-secondary'\n    case 'danger':\n      return 'btn-danger'\n    case 'ghost':\n      return 'btn-ghost'\n  }\n}\n\n\/\/ \u30e6\u30fc\u30b6\u30fc\u5b9a\u7fa9\u578b\u30ac\u30fc\u30c9\ntype Category = 'technology' | 'health' | 'finance'\n\nfunction isCategory(value: string): value is Category {\n  return ['technology', 'health', 'finance'].includes(value)\n}\n\n\/\/ \u4f7f\u3044\u65b9\uff08unknown\u304b\u3089\u5b89\u5168\u306bCategory\u578b\u306b\u5909\u63db\uff09\nconst input = document.getElementById('category')?.getAttribute('value') \/\/ string | null\n\nif (input && isCategory(input)) {\n  \/\/ \u3053\u3053\u3067\u306f input: Category \u3068\u3057\u3066\u6271\u3048\u308b\n  const config = getCategoryConfig(input)  \/\/ \u578b\u5b89\u5168\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Tailwind CSS\u9023\u643a\uff5cCVA\u3067\u578b\u5b89\u5168\u306a\u30d0\u30ea\u30a2\u30f3\u30c8\u7ba1\u7406<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ cva (class-variance-authority) \u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\n\/\/ npm install class-variance-authority\n\n\/\/ components\/Button\/Button.tsx\nimport { cva, type VariantProps } from 'class-variance-authority'\n\n\/\/ \u30d0\u30ea\u30a2\u30f3\u30c8\u306e\u5b9a\u7fa9\uff08\u30ea\u30c6\u30e9\u30eb\u578b\u3067\u578b\u5b89\u5168\uff09\nconst buttonVariants = cva(\n  \/\/ \u30d9\u30fc\u30b9\u30af\u30e9\u30b9\uff08\u5e38\u306b\u9069\u7528\uff09\n  'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed',\n  {\n    variants: {\n      \/\/ \u30d0\u30ea\u30a2\u30f3\u30c8\u306e\u5b9a\u7fa9\n      variant: {\n        primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',\n        secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500',\n        danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',\n        ghost: 'bg-transparent hover:bg-gray-100 text-gray-700 focus:ring-gray-500',\n        outline: 'border-2 border-blue-600 text-blue-600 hover:bg-blue-50 focus:ring-blue-500',\n      },\n      size: {\n        sm: 'h-8 px-3 text-xs',\n        md: 'h-10 px-4 text-sm',\n        lg: 'h-12 px-6 text-base',\n        icon: 'h-10 w-10 p-0',\n      },\n      fullWidth: {\n        true: 'w-full',\n      },\n    },\n    \/\/ \u30d0\u30ea\u30a2\u30f3\u30c8\u306e\u7d44\u307f\u5408\u308f\u305b\u306b\u3088\u308b\u8ffd\u52a0\u30af\u30e9\u30b9\n    compoundVariants: [\n      {\n        variant: 'primary',\n        size: 'lg',\n        class: 'shadow-lg',\n      },\n      {\n        variant: 'ghost',\n        size: 'sm',\n        class: 'rounded-full',\n      },\n    ],\n    \/\/ \u30c7\u30d5\u30a9\u30eb\u30c8\u30d0\u30ea\u30a2\u30f3\u30c8\n    defaultVariants: {\n      variant: 'primary',\n      size: 'md',\n    },\n  }\n)\n\n\/\/ VariantProps \u3067\u578b\u3092\u81ea\u52d5\u53d6\u5f97\nexport interface ButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {\n  loading?: boolean\n  leftIcon?: React.ReactNode\n  rightIcon?: React.ReactNode\n}\n\nexport function Button({\n  className,\n  variant,\n  size,\n  fullWidth,\n  loading,\n  leftIcon,\n  rightIcon,\n  children,\n  disabled,\n  ...props\n}: ButtonProps) {\n  return (\n    <button\n      className={buttonVariants({ variant, size, fullWidth, className })}\n      disabled={disabled || loading}\n      aria-busy={loading}\n      {...props}\n    >\n      {loading ? (\n        <svg className=\"animate-spin h-4 w-4 mr-2\" viewBox=\"0 0 24 24\">\n          {\/* \u30b9\u30d4\u30ca\u30fcSVG *\/}\n        <\/svg>\n      ) : leftIcon}\n      {children}\n      {!loading && rightIcon}\n    <\/button>\n  )\n}\n\n\/\/ \u4f7f\u3044\u65b9\uff08\u578b\u88dc\u5b8c\u304c\u52b9\u304f\uff09\n\/\/ <Button variant=\"primary\" size=\"lg\">\u9001\u4fe1<\/Button>\n\/\/ <Button variant=\"danger\" size=\"sm\" fullWidth>\u524a\u9664<\/Button>\n\/\/ <Button variant=\"invalid\" \/> \u2190 \u274c TypeScript\u30a8\u30e9\u30fc<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u30c7\u30b6\u30a4\u30f3\u30c8\u30fc\u30af\u30f3\u306e\u578b\u5b89\u5168\u7ba1\u7406<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ design-tokens.ts\n\/\/ \u30c7\u30b6\u30a4\u30f3\u30c8\u30fc\u30af\u30f3\u3092TypeScript\u3067\u578b\u5b89\u5168\u306b\u7ba1\u7406\u3059\u308b\n\nconst tokens = {\n  \/\/ \u30ab\u30e9\u30fc\u30c8\u30fc\u30af\u30f3\n  color: {\n    brand: {\n      primary: '#3B82F6',   \/\/ Blue-500\n      secondary: '#8B5CF6', \/\/ Violet-500\n      accent: '#EC4899',    \/\/ Pink-500\n    },\n    semantic: {\n      success: '#10B981',  \/\/ Emerald-500\n      warning: '#F59E0B',  \/\/ Amber-500\n      error: '#EF4444',    \/\/ Red-500\n      info: '#6366F1',     \/\/ Indigo-500\n    },\n    neutral: {\n      '50': '#F9FAFB',\n      '100': '#F3F4F6',\n      '200': '#E5E7EB',\n      '300': '#D1D5DB',\n      '400': '#9CA3AF',\n      '500': '#6B7280',\n      '600': '#4B5563',\n      '700': '#374151',\n      '800': '#1F2937',\n      '900': '#111827',\n    },\n  },\n  \/\/ \u30b9\u30da\u30fc\u30b7\u30f3\u30b0\u30c8\u30fc\u30af\u30f3\n  space: {\n    '0': '0',\n    '1': '0.25rem',  \/\/ 4px\n    '2': '0.5rem',   \/\/ 8px\n    '3': '0.75rem',  \/\/ 12px\n    '4': '1rem',     \/\/ 16px\n    '5': '1.25rem',  \/\/ 20px\n    '6': '1.5rem',   \/\/ 24px\n    '8': '2rem',     \/\/ 32px\n    '10': '2.5rem',  \/\/ 40px\n    '12': '3rem',    \/\/ 48px\n    '16': '4rem',    \/\/ 64px\n  },\n  \/\/ \u30bf\u30a4\u30dd\u30b0\u30e9\u30d5\u30a3\u30c8\u30fc\u30af\u30f3\n  fontSize: {\n    xs: ['0.75rem', { lineHeight: '1rem' }],\n    sm: ['0.875rem', { lineHeight: '1.25rem' }],\n    base: ['1rem', { lineHeight: '1.5rem' }],\n    lg: ['1.125rem', { lineHeight: '1.75rem' }],\n    xl: ['1.25rem', { lineHeight: '1.75rem' }],\n    '2xl': ['1.5rem', { lineHeight: '2rem' }],\n    '3xl': ['1.875rem', { lineHeight: '2.25rem' }],\n    '4xl': ['2.25rem', { lineHeight: '2.5rem' }],\n  },\n  \/\/ \u30dc\u30fc\u30c0\u30fc\u30e9\u30b8\u30a6\u30b9\u30c8\u30fc\u30af\u30f3\n  borderRadius: {\n    none: '0',\n    sm: '0.125rem',\n    base: '0.25rem',\n    md: '0.375rem',\n    lg: '0.5rem',\n    xl: '0.75rem',\n    '2xl': '1rem',\n    full: '9999px',\n  },\n} as const\n\n\/\/ \u578b\u306e\u81ea\u52d5\u751f\u6210\ntype ColorBrand = keyof typeof tokens.color.brand\ntype ColorSemantic = keyof typeof tokens.color.semantic\ntype SpaceKey = keyof typeof tokens.space\ntype FontSizeKey = keyof typeof tokens.fontSize\ntype RadiusKey = keyof typeof tokens.borderRadius\n\n\/\/ \u30c8\u30fc\u30af\u30f3\u30a2\u30af\u30bb\u30b5\u30fc\u95a2\u6570\uff08\u578b\u5b89\u5168\uff09\nexport const token = {\n  color: {\n    brand: (key: ColorBrand) => tokens.color.brand[key],\n    semantic: (key: ColorSemantic) => tokens.color.semantic[key],\n  },\n  space: (key: SpaceKey) => tokens.space[key],\n  fontSize: (key: FontSizeKey) => tokens.fontSize[key],\n  radius: (key: RadiusKey) => tokens.borderRadius[key],\n}\n\n\/\/ \u4f7f\u3044\u65b9\nconst primary = token.color.brand('primary')   \/\/ \"#3B82F6\"\uff08\u578b: \"#3B82F6\"\uff09\nconst invalid = token.color.brand('tertiary')  \/\/ \u274c \u30a8\u30e9\u30fc: \u5b58\u5728\u3057\u306a\u3044\u30ad\u30fc<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">CSS-in-JS\uff08vanilla-extract\uff09\u3067\u306e\u30ea\u30c6\u30e9\u30eb\u578b\u6d3b\u7528<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ styles\/theme.css.ts\uff08vanilla-extract\uff09\n\nimport { createTheme, createGlobalTheme } from '@vanilla-extract\/css'\nimport { tokens } from '.\/design-tokens'\n\n\/\/ \u30c6\u30fc\u30de\u5909\u6570\u3092\u578b\u5b89\u5168\u306b\u5b9a\u7fa9\nexport const [lightTheme, vars] = createTheme({\n  color: {\n    primary: tokens.color.brand.primary,\n    secondary: tokens.color.brand.secondary,\n    text: tokens.color.neutral['900'],\n    textMuted: tokens.color.neutral['500'],\n    background: tokens.color.neutral['50'],\n    border: tokens.color.neutral['200'],\n  },\n  space: {\n    sm: tokens.space['2'],\n    md: tokens.space['4'],\n    lg: tokens.space['6'],\n    xl: tokens.space['8'],\n  },\n  fontFamily: {\n    sans: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n    mono: '\"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace',\n  },\n})\n\n\/\/ \u30c0\u30fc\u30af\u30c6\u30fc\u30de\uff08light theme\u306e\u5909\u6570\u3092\u4e0a\u66f8\u304d\uff09\nexport const darkTheme = createTheme(vars, {\n  color: {\n    primary: tokens.color.brand.secondary,\n    secondary: tokens.color.brand.primary,\n    text: tokens.color.neutral['50'],\n    textMuted: tokens.color.neutral['400'],\n    background: tokens.color.neutral['900'],\n    border: tokens.color.neutral['700'],\n  },\n  space: {\n    sm: tokens.space['2'],\n    md: tokens.space['4'],\n    lg: tokens.space['6'],\n    xl: tokens.space['8'],\n  },\n  fontFamily: {\n    sans: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n    mono: '\"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace',\n  },\n})<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ styles\/button.css.ts\uff08vanilla-extract \u3067\u306e\u30d0\u30ea\u30a2\u30f3\u30c8\u5b9a\u7fa9\uff09\n\nimport { style, styleVariants, composeStyles } from '@vanilla-extract\/css'\nimport { recipe, RecipeVariants } from '@vanilla-extract\/recipes'\nimport { vars } from '.\/theme.css'\n\nexport const button = recipe({\n  base: {\n    display: 'inline-flex',\n    alignItems: 'center',\n    justifyContent: 'center',\n    borderRadius: '0.375rem',\n    fontWeight: 600,\n    cursor: 'pointer',\n    transition: 'all 150ms',\n    border: 'none',\n    ':focus-visible': {\n      outline: `2px solid ${vars.color.primary}`,\n      outlineOffset: '2px',\n    },\n    ':disabled': {\n      opacity: 0.5,\n      cursor: 'not-allowed',\n    },\n  },\n  variants: {\n    variant: {\n      primary: {\n        backgroundColor: vars.color.primary,\n        color: 'white',\n      },\n      secondary: {\n        backgroundColor: 'transparent',\n        color: vars.color.text,\n        border: `1px solid ${vars.color.border}`,\n      },\n      ghost: {\n        backgroundColor: 'transparent',\n        color: vars.color.text,\n      },\n    },\n    size: {\n      sm: { height: '2rem', padding: `0 ${vars.space.sm}`, fontSize: '0.75rem' },\n      md: { height: '2.5rem', padding: `0 ${vars.space.md}`, fontSize: '0.875rem' },\n      lg: { height: '3rem', padding: `0 ${vars.space.lg}`, fontSize: '1rem' },\n    },\n  },\n  defaultVariants: {\n    variant: 'primary',\n    size: 'md',\n  },\n})\n\n\/\/ RecipeVariants\u3067\u578b\u3092\u81ea\u52d5\u53d6\u5f97\ntype ButtonVariants = RecipeVariants<typeof button>\n\/\/ { variant?: \"primary\" | \"secondary\" | \"ghost\"; size?: \"sm\" | \"md\" | \"lg\" }\n\n\/\/ React\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u3067\u306e\u4f7f\u7528\ninterface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, ButtonVariants {}\n\nexport function Button({ variant, size, className, ...props }: ButtonProps) {\n  return <button className={button({ variant, size })} {...props} \/>\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u30d6\u30e9\u30f3\u30c9\u578b\uff08Branded Types\uff09\uff5c\u540c\u3058\u578b\u306e\u5024\u3092\u533a\u5225\u3059\u308b<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>>\/\/ \u30d6\u30e9\u30f3\u30c9\u578b\uff1a\u540c\u3058\u578b\u3067\u3082\u610f\u5473\u306e\u7570\u306a\u308b\u5024\u3092\u533a\u5225\u3059\u308b\n\n\/\/ \u554f\u984c\uff1aPX\u3068REM\u306f\u4e21\u65b9number\u3060\u304c\u3001\u6df7\u5728\u3059\u308b\u3068\u30d0\u30b0\u306b\u306a\u308b\nfunction addSpacing(px: number, rem: number) {\n  return px + rem  \/\/ \u30d0\u30b0: \u5358\u4f4d\u304c\u9055\u3046\u306e\u306b\u52a0\u7b97\u3057\u3066\u3057\u307e\u3046\u53ef\u80fd\u6027\n}\n\n\/\/ \u89e3\u6c7a\uff1a\u30d6\u30e9\u30f3\u30c9\u578b\u3067\u533a\u5225\u3059\u308b\ntype Brand<T, TBrand extends string> = T & { __brand: TBrand }\n\ntype Px = Brand<number, 'Px'>\ntype Rem = Brand<number, 'Rem'>\ntype Hex = Brand<string, 'Hex'>  \/\/ \"#RRGGBB\"\u5f62\u5f0f\u306e\u8272\n\n\/\/ \u30d5\u30a1\u30af\u30c8\u30ea\u95a2\u6570\uff08\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u4ed8\u304d\uff09\nfunction px(value: number): Px {\n  return value as Px\n}\nfunction rem(value: number): Rem {\n  return value as Rem\n}\nfunction hex(value: string): Hex {\n  if (!\/^#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$\/.test(value)) {\n    throw new Error(`Invalid hex color: ${value}`)\n  }\n  return value as Hex\n}\n\n\/\/ \u578b\u5b89\u5168\u306a\u95a2\u6570\nfunction pxToRem(pixels: Px, baseFontSize: Px = px(16)): Rem {\n  return rem(pixels \/ baseFontSize)\n}\n\nfunction applyBackgroundColor(element: HTMLElement, color: Hex): void {\n  element.style.backgroundColor = color\n}\n\n\/\/ \u4f7f\u3044\u65b9\nconst spacing = px(16)          \/\/ \u578b: Px\nconst fontSize = rem(1.5)       \/\/ \u578b: Rem\nconst color = hex('#3B82F6')    \/\/ \u578b: Hex\n\napplyBackgroundColor(div, color)      \/\/ \u2705 OK\napplyBackgroundColor(div, '#3B82F6') \/\/ \u274c \u30a8\u30e9\u30fc: string \u306f Hex \u306b\u4ee3\u5165\u4e0d\u53ef\napplyBackgroundColor(div, '#gggggg') \/\/ \u274c hex() \u3067\u30e9\u30f3\u30bf\u30a4\u30e0\u30a8\u30e9\u30fc\uff08\u4e0d\u6b63\u306ahex\uff09<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u3088\u304f\u3042\u308b\u8cea\u554f<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u30ea\u30c6\u30e9\u30eb\u578b\u3068enum\u306f\u3069\u3061\u3089\u3092\u4f7f\u3046\u3079\u304d\u3067\u3059\u304b\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6570\u5024Enum\u306f\u907f\u3051\u3066\u304f\u3060\u3055\u3044\u3002\u30d0\u30f3\u30c9\u30eb\u30b5\u30a4\u30ba\u304c\u5897\u52a0\u3057\u3001\u5024\u304c\u6570\u5024\u306b\u306a\u308b\u305f\u3081JSON\u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u3067\u554f\u984c\u304c\u8d77\u304d\u3084\u3059\u3044\u3067\u3059\u3002\u30b7\u30f3\u30d7\u30eb\u306a\u5217\u6319\u306a\u3089\u6587\u5b57\u5217\u30ea\u30c6\u30e9\u30eb\u578b\uff08Union\uff09\u3001\u610f\u5473\u306e\u3042\u308b\u5b9a\u6570\u540d\u304c\u5fc5\u8981\u306a\u3089<code>const Object + as const<\/code>\u306eEnum\u30d1\u30bf\u30fc\u30f3\u304c\u30d9\u30b9\u30c8\u3067\u3059\u3002\u30c1\u30fc\u30e0\u3067\u65b9\u91dd\u3092\u7d71\u4e00\u3059\u308b\u3053\u3068\u304c\u6700\u3082\u91cd\u8981\u3067\u3001\u3069\u3061\u3089\u3082\u9593\u9055\u3044\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Tailwind\u3068TypeScript\u306e\u30ea\u30c6\u30e9\u30eb\u578b\u306f\u3069\u3046\u7d44\u307f\u5408\u308f\u305b\u307e\u3059\u304b\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u3044\u304f\u3064\u304b\u306e\u30a2\u30d7\u30ed\u30fc\u30c1\u304c\u3042\u308a\u307e\u3059\u3002\u2460<code>cva<\/code>\uff08class-variance-authority\uff09\u3092\u4f7f\u3063\u305f\u30d0\u30ea\u30a2\u30f3\u30c8\u7ba1\u7406\uff08\u672c\u8a18\u4e8b\u306eButton\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u4f8b\uff09\u2461<code>tailwind-variants<\/code>\u30e9\u30a4\u30d6\u30e9\u30ea\uff08cva\u306e\u6a5f\u80fd\u5f37\u5316\u7248\uff09\u2462<code>clsx<\/code> + <code>tailwind-merge<\/code>\u306e\u7d44\u307f\u5408\u308f\u305b\u3067\u52d5\u7684\u30af\u30e9\u30b9\u751f\u6210\u3002\u30af\u30e9\u30b9\u540d\u6587\u5b57\u5217\u5168\u4f53\u3092\u578b\u4ed8\u3051\u3059\u308b\u306e\u306f\u96e3\u3057\u3044\u305f\u3081\u3001\u300c\u30d0\u30ea\u30a2\u30f3\u30c8\u540d\u300d\u3092\u30ea\u30c6\u30e9\u30eb\u578b\u3067\u7ba1\u7406\u3057\u3001\u30af\u30e9\u30b9\u6587\u5b57\u5217\u3078\u306e\u5909\u63db\u306fcva\u306a\u3069\u306e\u30e9\u30a4\u30d6\u30e9\u30ea\u306b\u4efb\u305b\u308b\u30a2\u30d7\u30ed\u30fc\u30c1\u304c\u73fe\u5b9f\u7684\u3067\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30ea\u30c6\u30e9\u30eb\u578b\u3067Tailwind\u306e\u5168\u30af\u30e9\u30b9\u3092\u578b\u5b9a\u7fa9\u3067\u304d\u307e\u3059\u304b\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u7406\u8ad6\u4e0a\u306f\u53ef\u80fd\u3067\u3059\u304c\u3001Tailwind\u306f1\u4e07\u7a2e\u985e\u4ee5\u4e0a\u306e\u30af\u30e9\u30b9\u304c\u3042\u308b\u305f\u3081\u3001\u5168\u30af\u30e9\u30b9\u3092\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30ea\u30c6\u30e9\u30eb\u578b\u3067\u5b9a\u7fa9\u3059\u308b\u3068TypeScript\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306b\u6df1\u523b\u306a\u5f71\u97ff\u304c\u51fa\u307e\u3059\u3002\u4ee3\u308f\u308a\u306b<strong>tailwind-classnames<\/strong>\u3084<strong>tailwind-ts<\/strong>\u306a\u3069\u306e\u30e9\u30a4\u30d6\u30e9\u30ea\u304c\u81ea\u52d5\u751f\u6210\u3057\u305f\u578b\u5b9a\u7fa9\u3092\u5229\u7528\u3059\u308b\u304b\u3001cva\u3067\u30d0\u30ea\u30a2\u30f3\u30c8\u5358\u4f4d\u3067\u7ba1\u7406\u3059\u308b\u65b9\u6cd5\u3092\u63a8\u5968\u3057\u307e\u3059\u3002\u300c\u3069\u306e\u30af\u30e9\u30b9\u3092\u4f7f\u3046\u304b\u300d\u3092\u5236\u9650\u3059\u308b\u3088\u308a\u3082\u300c\u3069\u306e\u30d0\u30ea\u30a2\u30f3\u30c8\u3092\u9078\u3076\u304b\u300d\u3092\u5236\u9650\u3059\u308b\u65b9\u304c\u7ba1\u7406\u3057\u3084\u3059\u3044\u3067\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">vanilla-extract\u3068Panda CSS\u3067\u306f\u3069\u3061\u3089\u3092\u9078\u3079\u3070\u3044\u3044\u3067\u3059\u304b\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>vanilla-extract<\/strong>\u306f\u6210\u719f\u3057\u305f\u9078\u629e\u80a2\u3067\u3001TypeScript\u5b8c\u5168\u5bfe\u5fdc\u30fb\u30bc\u30ed\u30e9\u30f3\u30bf\u30a4\u30e0\u30fbSSR\u5bfe\u5fdc\u304c\u7279\u5fb4\u3067\u3059\u3002Next.js\u3084Astro\u3068\u306e\u7d71\u5408\u4e8b\u4f8b\u304c\u8c4a\u5bcc\u3067\u3001\u65e2\u5b58CSS-in-JS\u304b\u3089\u306e\u79fb\u884c\u306b\u3082\u5411\u3044\u3066\u3044\u307e\u3059\u3002<strong>Panda CSS<\/strong>\u306fTailwind\u30e9\u30a4\u30af\u306a\u30c8\u30fc\u30af\u30f3\u8a2d\u8a08\u3067\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u30d9\u30fc\u30b9\u306e\u30a2\u30d7\u30ed\u30fc\u30c1\u3067\u3059\u3002\u65b0\u898f\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067Tailwind\u306e\u578b\u5b89\u5168\u7248\u3092\u6c42\u3081\u308b\u306a\u3089Panda CSS\u3001\u65e2\u5b58\u306eCSS-in-JS\u74b0\u5883\u3092\u79fb\u884c\u3059\u308b\u306a\u3089vanilla-extract\u3092\u9078\u3076\u3068\u826f\u3044\u3067\u3057\u3087\u3046\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u30d6\u30e9\u30f3\u30c9\u578b\u306f\u3044\u3064\u4f7f\u3046\u3079\u304d\u3067\u3059\u304b\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u540c\u3058\u30d7\u30ea\u30df\u30c6\u30a3\u30d6\u578b\uff08number\u3084string\uff09\u3067\u3082\u610f\u5473\u304c\u7570\u306a\u308b\u5024\u3092\u533a\u5225\u3057\u305f\u3044\u3068\u304d\u306b\u6709\u52b9\u3067\u3059\u3002\u4ee3\u8868\u7684\u306a\u30b1\u30fc\u30b9\u3068\u3057\u3066\u2460CSS\u5358\u4f4d\u306e\u6df7\u5728\u9632\u6b62\uff08Px\u3068Rem\uff09\u2461ID\u5024\u306e\u6df7\u5728\u9632\u6b62\uff08UserId vs ArticleId\uff09\u2462\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u6e08\u307f\u6587\u5b57\u5217\u306e\u7ba1\u7406\uff08Hex\u30ab\u30e9\u30fc\u30b3\u30fc\u30c9\u3084URL\uff09\u304c\u3042\u308a\u307e\u3059\u3002\u3059\u3079\u3066\u306e\u5024\u306b\u30d6\u30e9\u30f3\u30c9\u578b\u3092\u4ed8\u3051\u308b\u3068\u8907\u96d1\u306b\u306a\u308b\u305f\u3081\u3001\u30d0\u30b0\u304c\u8d77\u304d\u3084\u3059\u3044\u7b87\u6240\u306b\u9650\u5b9a\u3057\u3066\u5c0e\u5165\u3059\u308b\u306e\u304c\u304a\u3059\u3059\u3081\u3067\u3059\u3002<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u307e\u3068\u3081<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u30ea\u30c6\u30e9\u30eb\u578b<\/strong>\uff1a\u7279\u5b9a\u306e\u5024\u306e\u307f\u3092\u53d7\u3051\u4ed8\u3051\u308b\u578b\u5b9a\u7fa9\u3002\u30bf\u30a4\u30dd\u3092\u30b3\u30f3\u30d1\u30a4\u30eb\u6642\u306b\u691c\u51fa\u3067\u304d\u3001IDE\u306e\u88dc\u5b8c\u3082\u6a5f\u80fd\u3059\u308b<\/li>\n\n\n<li><strong>\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30ea\u30c6\u30e9\u30eb\u578b<\/strong>\uff1a\u6587\u5b57\u5217\u30ea\u30c6\u30e9\u30eb\u306e\u7d44\u307f\u5408\u308f\u305b\u3092\u578b\u3067\u8868\u73fe\u3002CSS\u30af\u30e9\u30b9\u540d\u306e\u52d5\u7684\u751f\u6210\u30fbAPI\u30a4\u30d9\u30f3\u30c8\u540d\u30d1\u30bf\u30fc\u30f3\u306e\u5b9a\u7fa9\u306b\u6709\u52b9<\/li>\n\n\n<li><strong>as const<\/strong>\uff1a\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u30ea\u30c6\u30e9\u30eb\u578b\u3068\u3057\u3066\u6271\u3046\u3002keyof typeof\u3068\u7d44\u307f\u5408\u308f\u305b\u3066\u30ad\u30fc\u30fb\u5024\u306e\u578b\u3092\u81ea\u52d5\u62bd\u51fa\u3067\u304d\u308b<\/li>\n\n\n<li><strong>Enum\u6bd4\u8f03<\/strong>\uff1a\u6570\u5024Enum\u306f\u907f\u3051\u308b\u3002\u30b7\u30f3\u30d7\u30eb\u306a\u5217\u6319\u306fUnion\u578b\u3001\u610f\u5473\u3042\u308b\u540d\u524d\u304c\u5fc5\u8981\u306a\u5834\u5408\u306fconst Object\uff08Enum\u4ee3\u66ff\uff09<\/li>\n\n\n<li><strong>\u578bnarrowing<\/strong>\uff1adiscriminated union\u3068\u578b\u30ac\u30fc\u30c9\u3067\u6761\u4ef6\u5206\u5c90\u5f8c\u306e\u578b\u3092\u7d5e\u308a\u8fbc\u3080\u3002exhaustiveCheck\u3067\u7db2\u7f85\u6027\u3092\u691c\u8a3c<\/li>\n\n\n<li><strong>Tailwind CVA<\/strong>\uff1acva\u3067\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u30d0\u30ea\u30a2\u30f3\u30c8\u3092\u578b\u5b89\u5168\u306b\u7ba1\u7406\u3002VariantProps\u3067\u578b\u3092\u81ea\u52d5\u53d6\u5f97\u3067\u304d\u308b<\/li>\n\n\n<li><strong>\u30c7\u30b6\u30a4\u30f3\u30c8\u30fc\u30af\u30f3<\/strong>\uff1aas const\u3067\u578b\u5b89\u5168\u306a\u30c8\u30fc\u30af\u30f3\u3092\u5b9a\u7fa9\u3002\u30a2\u30af\u30bb\u30b5\u30fc\u95a2\u6570\u3067\u5b58\u5728\u3057\u306a\u3044\u30c8\u30fc\u30af\u30f3\u3078\u306e\u30a2\u30af\u30bb\u30b9\u3092\u30b3\u30f3\u30d1\u30a4\u30eb\u6642\u306b\u30d6\u30ed\u30c3\u30af<\/li>\n\n\n<li><strong>vanilla-extract<\/strong>\uff1arecipeAPI\u3067\u30ea\u30c6\u30e9\u30eb\u578b\u4ed8\u304d\u306eCSS\u30d0\u30ea\u30a2\u30f3\u30c8\u3092\u5b9a\u7fa9\u3002RecipeVariants\u3067\u578b\u3092\u81ea\u52d5\u751f\u6210<\/li>\n\n\n<li><strong>\u30d6\u30e9\u30f3\u30c9\u578b<\/strong>\uff1a\u540c\u3058\u578b\u3067\u3082\u610f\u5473\u306e\u7570\u306a\u308b\u5024\u3092\u533a\u5225\uff08Px\u3068Rem\u3092\u6df7\u5728\u3055\u305b\u306a\u3044\u306a\u3069\uff09\u3002CSS\u5358\u4f4d\u306e\u578b\u5b89\u5168\u306a\u7ba1\u7406\u306b\u6709\u52b9<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><span class=\"swl-marker mark_yellow\">TypeScript\u306e\u30ea\u30c6\u30e9\u30eb\u578b\u3067CSS\u30af\u30e9\u30b9\u540d\u30fb\u30ab\u30e9\u30fc\u5024\u30fb\u30b9\u30da\u30fc\u30b7\u30f3\u30b0\u30fb\u30d0\u30ea\u30a2\u30f3\u30c8\u3092\u578b\u4ed8\u3051\u3059\u308b\u3053\u3068\u3067\u3001\u300c\u30b9\u30bf\u30a4\u30eb\u306e\u30bf\u30a4\u30dd\u300d\u300c\u5b58\u5728\u3057\u306a\u3044\u30d0\u30ea\u30a2\u30f3\u30c8\u306e\u6307\u5b9a\u300d\u300c\u30c7\u30b6\u30a4\u30f3\u30c8\u30fc\u30af\u30f3\u306e\u8aa4\u7528\u300d\u3068\u3044\u3046\u3088\u304f\u3042\u308b\u30d0\u30b0\u3092\u30b3\u30f3\u30d1\u30a4\u30eb\u6642\u306b\u9632\u3052\u307e\u3059\u3002\u307e\u305a\u306fCategory\u3084Status\u306e\u3088\u3046\u306a\u5217\u6319\u5024\u304b\u3089\u30ea\u30c6\u30e9\u30eb\u578b\u3078\u306e\u79fb\u884c\u3092\u59cb\u3081\u3066\u307f\u307e\u3057\u3087\u3046\u3002<\/span><\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">WithCode\u3092\u4f53\u9a13\u3067\u304d\u308b\u521d\u7d1a\u30b3\u30fc\u30b9\u516c\u958b\u4e2d\uff01<\/h2>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link has-white-color has-text-color has-background wp-element-button\" href=\"https:\/\/withcode.tech\/reservation\/\" style=\"background-color:#ffbf00\"><strong>\u516c\u5f0f\u30b5\u30a4\u30c8\u304b\u3089\u7121\u6599\u30ab\u30a6\u30f3\u30bb\u30ea\u30f3\u30b0\u306b\u7533\u3057\u8fbc\u3080 \u2192<\/strong><\/a><\/div>\n<\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>TypeScript\u306e\u30ea\u30c6\u30e9\u30eb\u578b\u30fb\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30ea\u30c6\u30e9\u30eb\u578b\u30fbconst\u30a2\u30b5\u30fc\u30b7\u30e7\u30f3\u3092\u5b8c\u5168\u89e3\u8aac\u3002Enum\u6bd4\u8f03\u30fb\u578bnarrowing\u30fbdiscriminated union\u30fbCVA\u30fbvanilla-extract\u30fb\u30d6\u30e9\u30f3\u30c9\u578b\u307e\u3067\u3001\u5b9f\u52d9\u3067\u4f7f\u3048\u308b\u578b\u5b89\u5168\u8a2d\u8a08\u30d1\u30bf\u30fc\u30f3\u3092\u7db2\u7f85\u3057\u307e\u3059\u3002<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"swell_btn_cv_data":"","footnotes":"","vk-ltc-link":"","vk-ltc-target":"0"},"categories":[34,359],"tags":[],"class_list":["post-13197","post","type-post","status-publish","format-standard","hentry","category-programming","category-javascript-programming"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/posts\/13197","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/comments?post=13197"}],"version-history":[{"count":2,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/posts\/13197\/revisions"}],"predecessor-version":[{"id":14003,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/posts\/13197\/revisions\/14003"}],"wp:attachment":[{"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/media?parent=13197"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/categories?post=13197"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/tags?post=13197"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}