FroquizFroquiz
HomeQuizzesSenior ChallengeGet CertifiedBlogAbout
Sign InStart Quiz
Sign InStart Quiz
Froquiz

The most comprehensive quiz platform for software engineers. Test yourself with 10000+ questions and advance your career.

LinkedIn

Platform

  • Start Quizzes
  • Topics
  • Blog
  • My Profile
  • Sign In

About

  • About Us
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Froquiz. All rights reserved.Built with passion for technology
Blog & Articles

What Is TypeScript? A JavaScript Developer's Real Introduction to the Type System

TypeScript isn't just "JavaScript with types." How to think in the type system, why avoiding any matters, how to use generics and utility types — learn to actually write TypeScript.

Yusuf SeyitoğluMarch 6, 20260 views10 min read

What Is TypeScript? A JavaScript Developer's Real Introduction to the Type System

JavaScript developers usually come to TypeScript one of two ways: because they had to, or because a production bug finally convinced them.

Either way, they hit the same first mistake: writing TypeScript like JavaScript, just adding occasional type annotations. This approach completely ignores TypeScript's most valuable features.

TypeScript isn't a linter — it's a design tool. The type system is a way of defining how your code behaves, and doing it well is a separate skill that needs to be learned.

Why TypeScript?

JavaScript is dynamically typed. A variable can start as a string and become a number. A function might not return the object you expected. This flexibility speeds up development but makes large codebases very hard to maintain.

javascript
// JavaScript: crashes at runtime function getUser(id) { return fetch(`/api/users/${id}`).then(r => r.json()); } const user = await getUser(123); console.log(user.naem); // undefined — typo, but no error sendEmail(user.email); // email could be undefined, nobody warned you

TypeScript catches these errors at compile time:

typescript
interface User { id: number; name: string; email: string; } async function getUser(id: number): Promise<User> { return fetch(`/api/users/${id}`).then(r => r.json()); } const user = await getUser(123); console.log(user.naem); // Error: Property "naem" does not exist on type "User" sendEmail(user.email); // email is always string, safe

Basic Types

typescript
// Primitives const name: string = "Alice"; const age: number = 25; const active: boolean = true; // Arrays const scores: number[] = [90, 85, 92]; const names: Array<string> = ["Alice", "Bob"]; // Tuple: fixed-length, fixed-type array const point: [number, number] = [10, 20]; // Any: disables the type system — avoid let data: any = "hello"; data = 42; // no error data.foo.bar; // no error — may crash at runtime // Unknown: safe alternative to any let input: unknown = getUserInput(); if (typeof input === "string") { input.toUpperCase(); // safe, type narrowed }

Using any is turning TypeScript off. Use unknown for genuinely unknown types — you're forced to check the type before using it.

Interface vs Type: When to Use Which?

typescript
// Interface: defines object shapes, extensible interface Animal { name: string; age: number; } interface Dog extends Animal { breed: string; } // Type: more flexible, powerful for unions and intersections type ID = string | number; type Status = "pending" | "active" | "cancelled"; type AdminUser = User & { adminLevel: number }; type Callback = (error: Error | null, result: string) => void;

Practical rule: prefer interface for object shapes, type for union types and complex type expressions.

Union and Intersection Types

typescript
// Union: A or B type Status = "success" | "error" | "loading"; function formatId(id: string | number): string { if (typeof id === "number") { return id.toString().padStart(6, "0"); } return id.toUpperCase(); } // Discriminated Union: powerful pattern type ApiResponse = | { status: "success"; data: User } | { status: "error"; message: string } | { status: "loading" }; function handleResponse(response: ApiResponse) { switch (response.status) { case "success": console.log(response.data.name); // TypeScript knows data exists break; case "error": console.log(response.message); // TypeScript knows message exists break; } }

Discriminated union is one of TypeScript's most powerful patterns. The compiler knows exactly what's available in each case.

Generics: Reusable Types

Generics let you write functions and classes that take type parameters. You don't have to rewrite the same logic for different types.

typescript
// Without generics: separate function for each type function getFirstString(arr: string[]): string { return arr[0]; } function getFirstNumber(arr: number[]): number { return arr[0]; } // With generics: one function for all types function getFirst<T>(arr: T[]): T { return arr[0]; } getFirst<string>(["a", "b", "c"]); // string getFirst<number>([1, 2, 3]); // number getFirst([true, false]); // boolean — inferred automatically // Generic interface interface ApiResponse<T> { data: T; status: number; message: string; } type UserResponse = ApiResponse<User>; type PostsResponse = ApiResponse<Post[]>; // Generic constraint function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user = { name: "Alice", age: 25 }; getProperty(user, "name"); // string getProperty(user, "age"); // number getProperty(user, "email"); // Error: "email" does not exist on type

Utility Types: Built-in Type Transformations

typescript
interface User { id: number; name: string; email: string; password: string; createdAt: Date; } type UserUpdate = Partial<User>; // all fields optional type RequiredUser = Required<User>; // all fields required type UserPreview = Pick<User, "id" | "name">; // { id, name } type PublicUser = Omit<User, "password">; // without password type ImmutableUser = Readonly<User>; // read-only fields type UserRoles = Record<string, "admin" | "editor" | "viewer">; // ReturnType: extract a function's return type function createUser(name: string, email: string) { return { id: Math.random(), name, email, createdAt: new Date() }; } type CreatedUser = ReturnType<typeof createUser>;

Utility types let you use the same interface in different contexts in different ways. Omit<User, "password"> strips the password from API responses. Partial<User> creates an optional update type for PATCH endpoints.

Actually Using TypeScript

The biggest trap when switching to TypeScript is gaming the type system. Silencing the compiler with any, bypassing type errors with as unknown as X, not leveraging type inference.

TypeScript's value comes from catching errors. To get that value, you have to take the type system seriously. Start with strict: true — it enables all strict checks. Challenging at first, pays off for every line of code it writes.

About Author

Yusuf Seyitoğlu

Author →

Other Posts

  • GraphQL Schema Design: Types, Resolvers, Mutations and Best PracticesMar 12
  • Java Collections Deep Dive: ArrayList, HashMap, TreeMap, LinkedHashMap and When to Use EachMar 12
  • CSS Advanced Techniques: Custom Properties, Container Queries, Grid Masonry and Modern LayoutsMar 12
All Blogs