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
+53
-36
@@ -1,49 +1,20 @@
|
||||
export interface TreeNode {
|
||||
// Fields shared by every node, regardless of category.
|
||||
interface BaseNode {
|
||||
id: string;
|
||||
label: string;
|
||||
description: string;
|
||||
x: number;
|
||||
y: number;
|
||||
parentId: string | null; // null means it's the root or connects to center
|
||||
category: 'origin' | 'experience' | 'projects' | 'skills' | 'education';
|
||||
size: 'x-large' | 'large' | 'medium' | 'small';
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
// ========== EXPERIENCE-SPECIFIC FIELDS ==========
|
||||
company?: string;
|
||||
jobTitle?: string;
|
||||
startDate?: string;
|
||||
endDate?: string; // Use "Present" for current role
|
||||
highlights?: string[]; // Key achievements or responsibilities
|
||||
|
||||
// ========== CONTRACT-SPECIFIC FIELDS ==========
|
||||
contractName?: string;
|
||||
client?: string;
|
||||
contractStart?: string;
|
||||
contractEnd?: string;
|
||||
|
||||
// ========== PROJECT-SPECIFIC FIELDS (extended) ==========
|
||||
techStack?: string[];
|
||||
projectUrl?: string;
|
||||
repoUrl?: string;
|
||||
npmUrl?: string; // For npm packages
|
||||
pypiUrl?: string; // For PyPI packages
|
||||
downloads?: string; // "1,000+ downloads"
|
||||
stars?: number; // GitHub stars
|
||||
status?: 'active' | 'maintained' | 'archived' | 'completed';
|
||||
|
||||
// ========== SKILL-SPECIFIC FIELDS ==========
|
||||
proficiency?: 'beginner' | 'intermediate' | 'advanced' | 'expert';
|
||||
yearsOfExperience?: number;
|
||||
|
||||
// ========== EDUCATION-SPECIFIC FIELDS ==========
|
||||
institution?: string;
|
||||
degree?: string;
|
||||
field?: string;
|
||||
graduationYear?: string;
|
||||
|
||||
// ========== ORIGIN-SPECIFIC FIELDS ==========
|
||||
// Center node with profile/contact info.
|
||||
export interface OriginNode extends BaseNode {
|
||||
category: 'origin';
|
||||
fullName?: string;
|
||||
jobTitle?: string;
|
||||
aboutMe?: string;
|
||||
location?: string;
|
||||
email?: string;
|
||||
@@ -53,6 +24,52 @@ export interface TreeNode {
|
||||
resumeUrl?: string;
|
||||
}
|
||||
|
||||
// Jobs and contract roles.
|
||||
export interface ExperienceNode extends BaseNode {
|
||||
category: 'experience';
|
||||
company?: string;
|
||||
jobTitle?: string;
|
||||
startDate?: string;
|
||||
endDate?: string; // Use "Present" for current role
|
||||
highlights?: string[]; // Key achievements or responsibilities
|
||||
contractName?: string;
|
||||
client?: string;
|
||||
contractStart?: string;
|
||||
contractEnd?: string;
|
||||
}
|
||||
|
||||
// Personal projects / packages.
|
||||
export interface ProjectNode extends BaseNode {
|
||||
category: 'projects';
|
||||
techStack?: string[];
|
||||
projectUrl?: string;
|
||||
repoUrl?: string;
|
||||
npmUrl?: string; // For npm packages
|
||||
pypiUrl?: string; // For PyPI packages
|
||||
downloads?: string; // "1,000+ downloads"
|
||||
stars?: number; // GitHub stars
|
||||
status?: 'active' | 'maintained' | 'archived' | 'completed';
|
||||
}
|
||||
|
||||
// Technical skills with proficiency.
|
||||
export interface SkillNode extends BaseNode {
|
||||
category: 'skills';
|
||||
proficiency?: 'beginner' | 'intermediate' | 'advanced' | 'expert';
|
||||
yearsOfExperience?: number;
|
||||
}
|
||||
|
||||
// Academic background.
|
||||
export interface EducationNode extends BaseNode {
|
||||
category: 'education';
|
||||
institution?: string;
|
||||
degree?: string;
|
||||
field?: string;
|
||||
graduationYear?: string;
|
||||
}
|
||||
|
||||
// Discriminated union over `category` — narrow on it to access category fields.
|
||||
export type TreeNode = OriginNode | ExperienceNode | ProjectNode | SkillNode | EducationNode;
|
||||
|
||||
// The center of our tree is at (0, 0)
|
||||
// Positive X = right, Negative X = left
|
||||
// Positive Y = down, Negative Y = up
|
||||
|
||||
Reference in new issue
Block a user