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
@@ -2,7 +2,7 @@
|
||||
// STATS UTILS — pure aggregation for the RPG-style character panel.
|
||||
// ==========================================================================
|
||||
|
||||
import { treeNodes, type TreeNode } from '$lib/data';
|
||||
import { treeNodes, type SkillNode } from '$lib/data';
|
||||
|
||||
/** Skill branch ids and their display labels (single source of truth). */
|
||||
export const BRANCH_IDS = [
|
||||
@@ -40,17 +40,17 @@ export function calculatePlayerLevel(): number {
|
||||
|
||||
/** Character "class" — the origin node's job title. */
|
||||
export function getPlayerClass(): string {
|
||||
const originNode = treeNodes.find((node) => node.id === 'origin');
|
||||
const originNode = treeNodes.find((node) => node.category === 'origin');
|
||||
return originNode?.jobTitle || 'Software Engineer';
|
||||
}
|
||||
|
||||
/** Group skill nodes by branch, sorted by proficiency. */
|
||||
export function aggregateSkills(): Record<string, { label: string; skills: TreeNode[] }> {
|
||||
const result: Record<string, { label: string; skills: TreeNode[] }> = {};
|
||||
export function aggregateSkills(): Record<string, { label: string; skills: SkillNode[] }> {
|
||||
const result: Record<string, { label: string; skills: SkillNode[] }> = {};
|
||||
|
||||
BRANCH_IDS.forEach((branchId) => {
|
||||
const skills = treeNodes.filter(
|
||||
(node) => node.parentId === branchId && node.category === 'skills'
|
||||
(node): node is SkillNode => node.parentId === branchId && node.category === 'skills'
|
||||
);
|
||||
|
||||
skills.sort((a, b) => {
|
||||
@@ -74,7 +74,7 @@ export function countByProficiency(): {
|
||||
} {
|
||||
const counts = { expert: 0, advanced: 0, intermediate: 0, beginner: 0 };
|
||||
treeNodes.forEach((node) => {
|
||||
if (node.proficiency) {
|
||||
if (node.category === 'skills' && node.proficiency) {
|
||||
counts[node.proficiency]++;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user