refactor: model TreeNode as a discriminated union
Phase 3 (behavior-preserving): - Replace the 45-optional-field TreeNode interface with a tagged union over category: OriginNode | ExperienceNode | ProjectNode | SkillNode | EducationNode, sharing a BaseNode. Category-specific fields now live only on their member. - Modal template narrows by category (category === 'experience' && ...) instead of bare field-presence checks; equivalent at runtime since each field is exclusive to its category (proven by the union compiling against the data). - stats utils narrow correctly: aggregateSkills filters to SkillNode[] via a type predicate; countByProficiency and getPlayerClass guard on category. svelte-check: 0 errors (every data node conforms). lint/test/build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
6091c62654
commit
bf235680a4
4 files changed
+68
-51
No files matched your search
@@ -1240,7 +1240,7 @@
|
||||
{/if}
|
||||
|
||||
<!-- Experience-specific content -->
|
||||
{#if selectedNode.company}
|
||||
{#if selectedNode.category === 'experience' && selectedNode.company}
|
||||
<div class="meta-info">
|
||||
<span class="company">{selectedNode.company}</span>
|
||||
{#if selectedNode.startDate}
|
||||
@@ -1252,7 +1252,7 @@
|
||||
{/if}
|
||||
|
||||
<!-- Education-specific content -->
|
||||
{#if selectedNode.institution}
|
||||
{#if selectedNode.category === 'education' && selectedNode.institution}
|
||||
<div class="meta-info">
|
||||
<span class="company">{selectedNode.institution}</span>
|
||||
{#if selectedNode.degree}
|
||||
@@ -1265,7 +1265,7 @@
|
||||
{/if}
|
||||
|
||||
<!-- Skill-specific content -->
|
||||
{#if selectedNode.proficiency}
|
||||
{#if selectedNode.category === 'skills' && selectedNode.proficiency}
|
||||
<div class="meta-info">
|
||||
<span class="proficiency-badge {selectedNode.proficiency}"
|
||||
>{selectedNode.proficiency}</span
|
||||
@@ -1281,7 +1281,7 @@
|
||||
{/if}
|
||||
|
||||
<!-- Highlights (for experience) -->
|
||||
{#if selectedNode.highlights && selectedNode.highlights.length > 0}
|
||||
{#if selectedNode.category === 'experience' && selectedNode.highlights && selectedNode.highlights.length > 0}
|
||||
<ul class="highlights">
|
||||
{#each selectedNode.highlights as highlight (highlight)}
|
||||
<li>{highlight}</li>
|
||||
@@ -1290,7 +1290,7 @@
|
||||
{/if}
|
||||
|
||||
<!-- Tech stack (for projects) -->
|
||||
{#if selectedNode.techStack && selectedNode.techStack.length > 0}
|
||||
{#if selectedNode.category === 'projects' && selectedNode.techStack && selectedNode.techStack.length > 0}
|
||||
<div class="tech-stack">
|
||||
{#each selectedNode.techStack as tech (tech)}
|
||||
<span class="tech-tag">{tech}</span>
|
||||
@@ -1299,14 +1299,14 @@
|
||||
{/if}
|
||||
|
||||
<!-- Project status badge -->
|
||||
{#if selectedNode.status}
|
||||
{#if selectedNode.category === 'projects' && selectedNode.status}
|
||||
<div class="project-status">
|
||||
<span class="status-badge {selectedNode.status}">{selectedNode.status}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Links (for projects) -->
|
||||
{#if selectedNode.projectUrl || selectedNode.repoUrl || selectedNode.pypiUrl || selectedNode.npmUrl}
|
||||
{#if selectedNode.category === 'projects' && (selectedNode.projectUrl || selectedNode.repoUrl || selectedNode.pypiUrl || selectedNode.npmUrl)}
|
||||
<div class="links">
|
||||
{#if selectedNode.projectUrl}
|
||||
<a
|
||||
|
||||
Reference in new issue
Block a user