Practice Apps
TypeScript and web development practice projects — app_2_kimi, heda1, heda2
Overview
This entry covers a collection of three practice web applications built while learning TypeScript and modern front-end development patterns. app_2_kimi is a TypeScript utility application that introduced the full TypeScript toolchain: npm project initialisation, tsconfig.json configuration, writing typed interfaces and functions, and compiling through tsc. heda1 is an HTML/CSS/JavaScript exercise focusing on interactive DOM manipulation and CSS layout practice with Flexbox. heda2 builds on heda1 with more advanced CSS Grid + Flexbox combined layouts and deeper JavaScript DOM patterns. Each project followed a consistent write → compile/lint → browser-test → refactor cycle, and the repositories document the progression from simple JavaScript patterns toward type-safe TypeScript development practices.
Architecture
Tech Stack
- TypeScript 5 — Strict mode compiler, interfaces, type aliases, function parameter types, return type annotations
- tsc (TypeScript Compiler) —
tsconfig.jsonwithstrict: true,target: ES2020,outDir: dist,rootDir: src - npm — Package management,
package.jsonscripts forbuildandwatchmodes - HTML5 — Semantic markup for interactive UI elements in heda1 and heda2
- CSS3 — Flexbox (heda1), CSS Grid combined with Flexbox (heda2)
- Vanilla JavaScript / TypeScript — DOM manipulation, event listeners, dynamic element creation and insertion
Build Process
Initialised the project with npm init -y and installed TypeScript as a dev dependency. Generated a tsconfig.json using npx tsc --init and configured the key compiler options for a browser-targeted project with strict checking enabled.
// tsconfig.json (key settings)
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"strict": true,
"rootDir": "./src",
"outDir": "./dist",
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
// package.json scripts
"scripts": {
"build": "tsc",
"watch": "tsc --watch"
}
Defined TypeScript interfaces for the application's data structures. All function parameters and return types were explicitly annotated, forcing a design-first approach to function signatures and making the compiler catch shape mismatches at build time rather than at runtime.
// src/types.ts
interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user';
}
// src/utils.ts
function formatUser(user: User): string {
return `${user.name} <${user.email}> [${user.role}]`;
}
function filterByRole(users: User[], role: User['role']): User[] {
return users.filter(u => u.role === role);
}
Built a single-page practice app focusing on Flexbox layout patterns and JavaScript DOM manipulation. Interactive elements (buttons, inputs) were used as hooks to practice addEventListener, reading input values, creating new DOM elements with document.createElement, and inserting them with appendChild / insertBefore.
// heda1 — core pattern practiced
const addBtn = document.getElementById('add-item');
const listInput = document.getElementById('item-input');
const list = document.getElementById('item-list');
addBtn.addEventListener('click', () => {
const text = listInput.value.trim();
if (!text) return;
const li = document.createElement('li');
li.textContent = text;
li.addEventListener('click', () => li.remove());
list.appendChild(li);
listInput.value = '';
});
Advanced the layout complexity by combining CSS Grid for macro page structure (header / sidebar / main / footer areas) with Flexbox for micro-component layout (nav items, card content). Practiced the pattern of using Grid for two-dimensional layout and Flexbox for one-dimensional alignment within Grid children.
/* heda2 — Grid macro layout */
.app-layout {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 240px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
/* Flexbox within grid children */
.sidebar-nav {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 1rem;
}
Each project used the same development loop: write TypeScript or JavaScript, run tsc --watch (or open the browser directly for plain JS), observe errors in the terminal or console, fix them, and refactor the code toward cleaner patterns. This cycle reinforced the habit of treating compiler errors and console warnings as primary feedback rather than treating them as noise to be dismissed.
Each project was managed in its own Git repository. Work was done on topic branches (feat/type-definitions, fix/strict-errors) with commit messages written to describe the intent of the change rather than the mechanical action ("Add User interface and filter utility" rather than "update types.ts"). This produced a commit history that reads as a learning narrative.
TypeScript Development Cycle
Challenges & Solutions
TypeScript Strict Mode Compiler Errors. Enabling "strict": true in tsconfig.json activates several sub-checks including strictNullChecks, noImplicitAny, and strictFunctionTypes. The initial codebase had several implicit any parameters and unchecked null returns from document.getElementById. The discipline required to satisfy the strict compiler — adding explicit types to every parameter, narrowing nullable DOM queries with type guards — was initially frustrating but produced significantly more robust code that caught real bugs at compile time.
Module Resolution with Older tsconfig Targets. Targeting ES5 while using ES6 module syntax (import/export) caused module resolution errors because the TypeScript compiler could not reconcile the CommonJS output format with native ES module syntax in the browser. The fix was to align the target and module settings — using ES2020 for both — and to serve the project via a local development server rather than opening the compiled HTML as a file:// URL, which does not support ES modules due to CORS restrictions.
What I Learned
- Setting up a TypeScript project from scratch:
npm init,tsc --init, configuringtsconfig.jsonrootDir,outDir,target, andmoduleoptions - TypeScript interfaces define the shape of objects and create a compile-time contract between the data model and the code that uses it
strict: trueis the correct default for new TypeScript projects — the additional friction of satisfying the compiler pays dividends in caught bugsdocument.getElementByIdreturnsHTMLElement | null— null checks or non-null assertions are required in strict TypeScript before accessing element properties- CSS Grid is best used for two-dimensional page-level layouts; Flexbox is best used for one-dimensional component-level alignment; combining both at different levels of the DOM is the standard production pattern
- Meaningful commit messages that describe intent (not mechanism) produce a version history that is genuinely useful for understanding how a project evolved