![]() |
๐งฉ Goal
Learn to structure a realistic application with TypeScript, integrating:
modules
interface and types
error handling
data access (e.g. from an API)
project organizzation
๐ Project structure
A good starting point would be an app that manages users.
Basic project structure:
users-app/
├── src/
│ ├── models/
│ │ └── User.ts
│ ├── services/
│ │ └── UserService.ts
│ ├── utils/
│ │ └── logger.ts
│ └── index.ts
├── tsconfig.json
├── package.json
๐ค 1. Definiton of User
model
src/models/User.ts
:
export interface User {
id: number;
name: string;
email: string;
isActive: boolean;
}
๐ This allows us to ensure that wherever we use a User, it always respects this structure.
๐ ️ 2. Create a service
src/services/UserService.ts
:
import { User } from "../models/User";
export class UserService {
private users: User[] = [];
add(user: User): void {
this.users.push(user);
}
findByEmail(email: string): User | undefined {
return this.users.find(u => u.email === email);
}
getActiveUsers(): User[] {
return this.users.filter(u => u.isActive);
}
}
Note:
-
User | undefined
: function could found nothing -
Everythin is typed, clear and and verifiable
๐งฐ 3. Utilities and logging
src/utils/logger.ts
:
export function logInfo(msg: string): void {
console.log("[INFO]", msg);
}
export function logErrore(msg: string): void {
console.error("[ERRORE]", msg);
}
๐ 4. Entry point: index.ts
src/index.ts
:
import { UserService } from "./services/UserService";
import { logInfo, logErrore } from "./utils/logger";
const userService = new UserService();
userService.add({
id: 1,
name: "Elisa",
email: "elisa@example.com",
isActive: true,
});
const user = userService.findByEmail("elisa@example.com");
if (user) {
logInfo(`User found: ${user.name}`);
} else {
logErrore("User not found");
}
๐ This is already a mini backend (or part of a business logic) entirely written in TypeScript, readable, scalable and secure.
๐ 5. Error Handling and Custom Types
TypeScript also allows you to type errors, or create more robust wrappers for common situations:
type Risultato<T> =
| { ok: true; value: T }
| { ok: false; error: string };
function findUser(email: string): Result<User> {
const u = userService.findByEmail(email);
if (u) {
return { ok: true, value: u };
} else {
return { ok: false, error: "No user found" };
}
}
๐งช 6. Example: fetch + async/await
+ types
Let's simulate a fetch to a REST API (JSONPlaceholder type).
interface Todo {
id: number;
title: string;
completed: boolean;
}
async function getTodos(): Promise<Todo[]> {
const res = await fetch("https://jsonplaceholder.typicode.com/todos");
if (!res.ok) throw new Error("Error retrieving data");
return await res.json();
}
Clear types, solid structure, error handling: all in true TypeScript style.
๐ 7. Integration with existing JS modules
You can use existing JavaScript libraries by typing them:
npm install axios
npm install --save-dev @types/axios
Then use it as if it were native TypeScript:
import axios from "axios";
async function getUsers(): Promise<User[]> {
const res = await axios.get<User[]>("https://api.miosito.it/users");
return res.data;
}
๐ 8. Real benefits in a project
✅ Drastically reduce bugs from “undefined”
✅ Intelligent autocompletion across the entire codebase
✅ More readable, maintainable, documented code
✅ Reduce trivial tests: types already do a lot of validation
✅ Easier to Refactor Safely
๐ In summary
Building a real project in TypeScript allows you to:
-
Define clear and reusable contracts
-
Avoid common mistakes
-
Write more scalable code
-
Collaborate better with teams (or… with yourself in 3 months ๐)
Follow me #techelopment
Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
X: techelopment
Bluesky: @techelopment
telegram: @techelopment_channel
whatsapp: Techelopment
youtube: @techelopment