5.3 אינטרפייסים וטייפים פתרון
פתרון - אינטרפייסים וטייפים¶
פתרון תרגיל 1¶
א. אינטרפייס Book:
interface Book {
title: string;
author: string;
pages: number;
isbn: string;
publishedYear: number;
}
let book1: Book = {
title: "Clean Code",
author: "Robert C. Martin",
pages: 464,
isbn: "978-0132350884",
publishedYear: 2008
};
let book2: Book = {
title: "The Pragmatic Programmer",
author: "David Thomas",
pages: 352,
isbn: "978-0135957059",
publishedYear: 1999
};
ב. formatBook:
function formatBook(book: Book): string {
return `${book.title} by ${book.author} (${book.publishedYear}) - ${book.pages} pages`;
}
ג. getOldestBook:
function getOldestBook(books: Book[]): Book {
let oldest = books[0];
for (let book of books) {
if (book.publishedYear < oldest.publishedYear) {
oldest = book;
}
}
return oldest;
}
פתרון תרגיל 2¶
א. אינטרפייס UserProfile:
interface UserProfile {
readonly id: number;
readonly username: string;
email: string;
bio?: string;
website?: string;
readonly createdAt: string;
}
ב. ניסיון שינוי:
let profile: UserProfile = {
id: 1,
username: "alice",
email: "alice@example.com",
createdAt: "2025-01-01"
};
profile.id = 2; // ERROR: Cannot assign to 'id' because it is a read-only property
profile.email = "new@example.com"; // OK - email is not readonly
ג. updateProfile:
function updateProfile(
profile: UserProfile,
updates: { email?: string; bio?: string; website?: string }
): UserProfile {
return {
...profile,
...updates
};
}
let updated = updateProfile(profile, { email: "new@example.com", bio: "Hello!" });
פתרון תרגיל 3¶
א. נקודה - interface כי זה אובייקט (אפשר גם type, שניהם תקינים):
ב. HttpMethod - type כי זה union:
ג. ApiError - interface כי זה אובייקט:
ד. Result - type כי זה union של אובייקטים:
ה. EventHandler - type כי זה פונקציה:
פתרון תרגיל 4¶
א. EnvVariables:
interface EnvVariables {
[key: string]: string;
}
let env: EnvVariables = {
NODE_ENV: "development",
PORT: "3000",
DATABASE_URL: "postgres://localhost:5432/mydb",
API_KEY: "abc123"
};
ב. ScoreBoard:
interface ScoreBoard {
[playerName: string]: number;
}
function getTopPlayer(scores: ScoreBoard): string {
let topPlayer = "";
let topScore = -Infinity;
for (let player in scores) {
if (scores[player] > topScore) {
topScore = scores[player];
topPlayer = player;
}
}
return topPlayer;
}
let scores: ScoreBoard = {
Alice: 150,
Bob: 230,
Charlie: 180
};
console.log(getTopPlayer(scores)); // "Bob"
ג. TypedDictionary:
interface TypedDictionary {
[key: string]: string | number; // must include number because of 'version'
version: number;
}
let dict: TypedDictionary = {
version: 1,
name: "my-config",
env: "production"
};
שימו לב: ה-index signature חייב להיות תואם לכל השדות המוגדרים. version הוא number, אז ה-index signature חייב לכלול number.
פתרון תרגיל 5¶
א. צורות עם extends ו-discriminator:
interface Shape {
color: string;
}
interface Circle extends Shape {
type: "circle";
radius: number;
}
interface Rectangle extends Shape {
type: "rectangle";
width: number;
height: number;
}
interface Triangle extends Shape {
type: "triangle";
base: number;
height: number;
}
type AnyShape = Circle | Rectangle | Triangle;
function getArea(shape: AnyShape): number {
switch (shape.type) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rectangle":
return shape.width * shape.height;
case "triangle":
return (shape.base * shape.height) / 2;
}
}
let circle: Circle = { type: "circle", color: "red", radius: 5 };
console.log(getArea(circle)); // 78.54
ב. TimestampedUser:
type User = {
name: string;
email: string;
};
type Timestamps = {
createdAt: string;
updatedAt: string;
};
type TimestampedUser = User & Timestamps;
let user: TimestampedUser = {
name: "Alice",
email: "alice@example.com",
createdAt: "2025-01-01",
updatedAt: "2025-06-15"
};
ג. קונפליקט ב-intersection:
type A = { x: number };
type B = { x: string };
type C = A & B;
// C.x is type 'never' - because there's no value that is both number AND string
// You can't create a valid object of type C:
let obj: C = { x: 42 }; // ERROR: Type 'number' is not assignable to type 'never'
פתרון תרגיל 6¶
interface Product {
name: string;
price: number;
category: string;
stock: number;
}
interface CartItem {
product: Product;
quantity: number;
}
interface Address {
street: string;
city: string;
zipCode: string;
}
interface Customer {
name: string;
email: string;
address: Address;
}
interface Order {
readonly id: string;
customer: Customer;
items: CartItem[];
status: "pending" | "shipped" | "delivered";
readonly createdAt: string;
}
function calculateOrderTotal(order: Order): number {
let total = 0;
for (let item of order.items) {
total += item.product.price * item.quantity;
}
return total;
}
function getOrderSummary(order: Order): string {
let itemCount = order.items.reduce((sum, item) => sum + item.quantity, 0);
let total = calculateOrderTotal(order);
return `Order for ${order.customer.name}: ${itemCount} items, total $${total.toFixed(2)}`;
}
פתרון תרגיל 7¶
א. Logger:
interface Logger {
info(message: string): void;
warn(message: string): void;
error(message: string, error?: Error): void;
}
let logger: Logger = {
info(message: string): void {
console.log(`[INFO] ${message}`);
},
warn(message: string): void {
console.log(`[WARN] ${message}`);
},
error(message: string, error?: Error): void {
console.log(`[ERROR] ${message}`);
if (error) {
console.log(` Stack: ${error.stack}`);
}
}
};
logger.info("Server started");
logger.warn("Disk space low");
logger.error("Connection failed", new Error("timeout"));
ב. Validator:
type Validator = (value: string) => { valid: boolean; message?: string };
let isNotEmpty: Validator = (value) => {
if (value.trim() === "") {
return { valid: false, message: "Value must not be empty" };
}
return { valid: true };
};
let isEmail: Validator = (value) => {
if (!value.includes("@")) {
return { valid: false, message: "Value must be a valid email" };
}
return { valid: true };
};
function isMinLength(min: number): Validator {
return (value) => {
if (value.length < min) {
return { valid: false, message: `Value must be at least ${min} characters` };
}
return { valid: true };
};
}
function validate(value: string, validators: Validator[]): string[] {
let errors: string[] = [];
for (let validator of validators) {
let result = validator(value);
if (!result.valid && result.message) {
errors.push(result.message);
}
}
return errors;
}
// usage
let errors = validate("", [isNotEmpty, isEmail, isMinLength(5)]);
console.log(errors);
// ["Value must not be empty", "Value must be a valid email", "Value must be at least 5 characters"]
let errors2 = validate("hi@a", [isNotEmpty, isEmail, isMinLength(5)]);
console.log(errors2);
// ["Value must be at least 5 characters"]