A small, framework-free TypeScript app demonstrating a simple CRUD flow against an in-memory store and DOM rendering with Bootstrap 5.
- TypeScript modules compiled to ES modules (no bundler required)
- A lightweight store (singleton) that holds movies, renders a list, and wires UI
- Add, Edit, Delete for movies using one shared form
- Event delegation to handle Edit/Delete buttons
- Empty-state rendering (“There is no movie yet, please add.”)
- Strict typing patterns with
exactOptionalPropertyTypes
- TypeScript (strict,
exactOptionalPropertyTypes: true) - Bootstrap 5 (CSS/JS from local assets)
- Plain ES modules (no bundler, no framework)
index.html– Bootstrap page with header/footer mounts, a movie form, and a listsrc/– TypeScript sourcescommon/– small UI modules (header, toggle, footer)classes/movies.ts– MoviesStore (add/edit/delete, rendering, form/list wiring)app.ts– app bootstrap that mounts UI and wires the store to the page
js/– compiled JavaScript output from TypeScript (outDirintsconfig.json)assets/– Bootstrap CSS/JS and helper scripts
- Compile TypeScript
cd C:\wamp64\www\TypingScriptCRUDDemo
tsc -p .- Open the app
- Double-click
index.htmlor serve the folder with any static server
- The “Movies” card (right side) shows the list; initially displays an empty-state message
- The “Movie Form” card (left side) adds a movie with: Title, Year, Rating, Genre, Director
- Submit to add; the movie appears in the list
- Each list item has “Edit” and “Delete”:
- Edit: loads the movie data into the form, button switches to “Update”; submit to save changes
- Delete: removes the movie from the store and list
src/classes/movies.ts(MoviesStore)addMovie()– adds movie, only sets optional fields when they exist (to satisfyexactOptionalPropertyTypes)renderTo()– DOM rendering of list items, adds action buttons, empty stateattachForm('movie-form')– binds to the form and handles add vs updateattachToListById('movies-list')– binds and delegates Edit/Delete clicksupdateMovie()/removeMovie()– edit and delete logic
- How to structure small apps with TS modules and no bundler
- How to safely handle optional properties with
exactOptionalPropertyTypes - DOM rendering patterns and event delegation for dynamic lists
- Keeping UI state in a simple store, not in the DOM
Use these copy-paste prompts to recreate the app incrementally.
Create a static web project named “TypingScriptCRUDDemo”.
Add index.html using Bootstrap 5 (CSS/JS). Include mounts #app-toggle, #app-header, #app-footer, and a main section with a two-column layout: left has a Movie Form (Title, Year, Rating select, Genre select, Director); right has a Movies list (UL id="movies-list"). Load a module script js/app.js at the end.
Create tsconfig.json with:
- rootDir: ./src
- outDir: ./js
- target: ES6, module: ES2015
- strict: true, exactOptionalPropertyTypes: true, sourceMap + declaration on
Include ["src"], exclude ["js"].
Create src/common/header.ts, src/common/toggle.ts, src/common/footer.ts, each exporting a createX() function returning a Bootstrap DOM element.
Create src/app.ts to import the three common modules, render them into #app-header/#app-toggle/#app-footer on DOMContentLoaded.
Create src/classes/movies.ts with a MoviesStore singleton:
- addMovie(title, year, rating, genre, director)
- attachToListById('movies-list') and renderTo(ul)
- Only set optional fields when provided to satisfy exactOptionalPropertyTypes
Export getMovies() returning the singleton.
In src/app.ts import getMovies(), then on DOMContentLoaded:
- movies.attachToListById('movies-list')
- movies.attachForm('movie-form') to bind the form
Extend MoviesStore to render Edit/Delete buttons for each item and delegate clicks:
- Edit: loads into the form, submit updates
- Delete: removes item from the store and refreshes
Add empty-state text when no movies exist.
Run: tsc -p .
Open index.html in a browser. Add, edit, delete movies and observe list updates.