Skip to main content

πŸ“˜ TypeScript Basics

πŸ“– Definition​

TypeScript is a superset language of JavaScript developed by Microsoft. By adding a static type system to JavaScript, it detects errors at code writing time and enhances IDE autocomplete functionality. TypeScript code is compiled and converted to JavaScript for execution.

🎯 Understanding Through Analogies​

Contract Analogy​

Comparing TypeScript to a contract:

JavaScript (Verbal Promise)
A: "Let's meet tomorrow afternoon!"
B: "Okay, got it!"

The next day...
A: "Why didn't you come?"
B: "I thought it was 2 PM?"
A: "No, I said 5 PM!"

Problems:
- No specific time specified
- Misunderstanding occurs
- Problem discovered at execution time

---

TypeScript (Formal Contract)
Contract:
- Date: January 15, 2024
- Time: 5 PM sharp
- Location: Gangnam Station Exit 2
- Participants: A, B (types specified!)

Advantages:
- Clear agreement
- No misunderstanding possible
- Verified at contract writing time
- Legal effect

Factory Analogy​

JavaScript Factory (No Inspection)
Parts Receiving:
- Any part is OK
- No size check
- No standard check

Assembly Line:
- Problems discovered during assembly
- "This screw doesn't fit?"
- "This part size is strange?"

Result:
- Defective products
- Time/cost waste
- Customer complaints

---

TypeScript Factory (Strict Inspection)
Parts Receiving:
βœ“ Size measurement
βœ“ Standard check
βœ“ Quality inspection
βœ— Defective parts rejected

Assembly Line:
- Parts fit exactly
- Assembly without problems
- Fast production

Result:
- High-quality products
- Efficient production
- Customer satisfaction

LEGO Block Analogy​

JavaScript LEGO
- Can try fitting any block
- Only know if it fits by trying
- Confusing when building large creations

TypeScript LEGO
- Each block has a label
- "This is a 2x4 block"
- "Only 2x4 fits in this hole"
- Can't fit what doesn't match from the start

Advantages:
- Assemble accurately following instructions
- Prevent mistakes
- No problem even with large creations

βš™οΈ How It Works​

1. Static Type System​

// JavaScript (Dynamic Types)
let name = "John Kim";
name = 123; // OK! No problem
name = true; // OK! No problem
name = null; // OK! No problem

// Problem discovered during execution
function greet(person) {
return "Hello, " + person.name; // Don't know what person is
}

greet({ name: "John" }); // OK
greet("John"); // πŸ’₯ Runtime Error!
greet(null); // πŸ’₯ Runtime Error!

// TypeScript (Static Types)
let name: string = "John Kim";
name = 123; // ❌ Compile Error! (Type error)
name = true; // ❌ Compile Error!
name = null; // ❌ Compile Error!

// Specify function parameters and return type
function greet(person: { name: string }): string {
return "Hello, " + person.name;
}

greet({ name: "John" }); // βœ… OK
greet("John"); // ❌ Compile Error! (Type error)
greet(null); // ❌ Compile Error!

// Advantages:
// 1. Errors discovered at code writing time
// 2. IDE autocomplete
// 3. Safe refactoring
// 4. Acts as documentation

2. Compilation Process​

// TypeScript Code
// app.ts
interface User {
id: number;
name: string;
email: string;
}

function getUser(id: number): User {
return {
id: id,
name: "John Kim",
email: "kim@example.com"
};
}

const user: User = getUser(1);
console.log(user.name);

// ↓ TypeScript Compiler (tsc)

// JavaScript Code
// app.js
function getUser(id) {
return {
id: id,
name: "John Kim",
email: "kim@example.com"
};
}

var user = getUser(1);
console.log(user.name);

// Characteristics:
// 1. Type information removed
// 2. Can convert to ES5, ES6, ESNext, etc.
// 3. Runs in browser/Node.js
// 4. Source map generation (for debugging)

3. Type Inference​

// TypeScript automatically infers types

// Explicit types
let name: string = "John Kim";
let age: number = 25;

// Type inference (automatic)
let name = "John Kim"; // Inferred as string
let age = 25; // Inferred as number
let isActive = true; // Inferred as boolean

// Arrays
let numbers = [1, 2, 3]; // Inferred as number[]
let names = ["Kim", "Lee"]; // Inferred as string[]

// Objects
let user = {
name: "John Kim",
age: 25
};
// Inferred as { name: string; age: number }

// Function return type
function add(a: number, b: number) {
return a + b; // Inferred as returning number
}

// Advantages:
// - Reduces type writing
// - Still type safe
// - Improves readability

πŸ’‘ Real Examples​

Basic Types​

// 1. Primitive types
let name: string = "John Kim";
let age: number = 25;
let isStudent: boolean = true;
let nothing: null = null;
let notDefined: undefined = undefined;

// 2. Arrays
let numbers: number[] = [1, 2, 3, 4, 5];
let names: Array<string> = ["John Kim", "Jane Lee"];

// Mixed arrays
let mixed: (number | string)[] = [1, "two", 3, "four"];

// 3. Tuples (fixed length, type for each position)
let person: [string, number] = ["John Kim", 25];
let coordinate: [number, number] = [10, 20];

// ❌ Wrong usage
person = [25, "John Kim"]; // Wrong order
person = ["John Kim"]; // Wrong length
person = ["John Kim", 25, true]; // Wrong length

// 4. enum (enumeration)
enum Color {
Red, // 0
Green, // 1
Blue // 2
}

let color: Color = Color.Red;
console.log(color); // 0

// Assigning values
enum Status {
Pending = "PENDING",
Approved = "APPROVED",
Rejected = "REJECTED"
}

let status: Status = Status.Pending;
console.log(status); // "PENDING"

// 5. any (allows all types)
let anything: any = "string";
anything = 123; // OK
anything = true; // OK
anything = null; // OK

// ⚠️ any removes TypeScript advantages!
// Use only when necessary

// 6. unknown (safer than any)
let value: unknown = "string";

// ❌ Cannot use directly
// console.log(value.length);

// βœ… Use after type checking
if (typeof value === "string") {
console.log(value.length); // OK
}

// 7. void (no return value)
function logMessage(message: string): void {
console.log(message);
// no return
}

// 8. never (never returns)
function throwError(message: string): never {
throw new Error(message);
// Function never ends
}

function infiniteLoop(): never {
while (true) {
// Infinite loop
}
}

// 9. object
let obj: object = { name: "John Kim" };
let arr: object = [1, 2, 3];
let func: object = function() {};

Interfaces​

// Interface: Define object structure

// 1. Basic interface
interface User {
id: number;
name: string;
email: string;
}

const user: User = {
id: 1,
name: "John Kim",
email: "kim@example.com"
};

// ❌ Missing property
const invalidUser: User = {
id: 1,
name: "John Kim"
// email missing!
};

// 2. Optional properties
interface Product {
id: number;
name: string;
price: number;
description?: string; // Optional (can exist or not)
}

const product1: Product = {
id: 1,
name: "Laptop",
price: 1000000,
description: "High-performance laptop"
};

const product2: Product = {
id: 2,
name: "Mouse",
price: 30000
// description OK even without it
};

// 3. Readonly properties
interface Config {
readonly apiUrl: string;
readonly timeout: number;
}

const config: Config = {
apiUrl: "https://api.example.com",
timeout: 5000
};

// ❌ Cannot modify
config.apiUrl = "https://api2.example.com";

// 4. Function types
interface SearchFunc {
(query: string, page: number): string[];
}

const search: SearchFunc = (query, page) => {
// Implementation
return ["Result1", "Result2"];
};

// 5. Index signature (dynamic properties)
interface StringMap {
[key: string]: string;
}

const translations: StringMap = {
hello: "Hello",
goodbye: "Goodbye",
thanks: "Thanks"
// Both key and value are string
};

// 6. Interface extension (inheritance)
interface Person {
name: string;
age: number;
}

interface Student extends Person {
studentId: string;
grade: number;
}

const student: Student = {
name: "John Kim",
age: 20,
studentId: "2024001",
grade: 2
};

// 7. Extending multiple interfaces
interface Printable {
print(): void;
}

interface Scannable {
scan(): void;
}

interface Printer extends Printable, Scannable {
model: string;
}

const printer: Printer = {
model: "HP-1234",
print() {
console.log("Printing...");
},
scan() {
console.log("Scanning...");
}
};

Type Alias​

// Type Alias: Naming types

// 1. Basic type alias
type UserID = number;
type UserName = string;

let id: UserID = 123;
let name: UserName = "John Kim";

// 2. Union types
type Status = "pending" | "approved" | "rejected";

let orderStatus: Status = "pending";
orderStatus = "approved"; // OK
// orderStatus = "shipped"; // ❌ Error

// 3. Object types
type Point = {
x: number;
y: number;
};

const point: Point = { x: 10, y: 20 };

// 4. Function types
type GreetFunction = (name: string) => string;

const greet: GreetFunction = (name) => {
return `Hello, ${name}!`;
};

// 5. Intersection types (intersection)
type Person = {
name: string;
age: number;
};

type Employee = {
employeeId: string;
department: string;
};

type EmployeePerson = Person & Employee;

const employee: EmployeePerson = {
name: "John Kim",
age: 30,
employeeId: "E123",
department: "Development"
// All properties required!
};

// 6. Interface vs Type
// Interface (extensible)
interface Animal {
name: string;
}

interface Animal {
age: number; // Merged!
}

const animal: Animal = {
name: "Dog",
age: 3
};

// Type (not extensible)
type Car = {
model: string;
};

// ❌ Error: Cannot duplicate declaration
// type Car = {
// year: number;
// };

// When to use which?
// - Interface: Object structure, classes, extensible
// - Type: Union, intersection, utility types

Generics​

// Generics: Reusable types

// ❌ Without generics (duplicate code)
function numberIdentity(value: number): number {
return value;
}

function stringIdentity(value: string): string {
return value;
}

function booleanIdentity(value: boolean): boolean {
return value;
}

// βœ… Using generics (unified into one)
function identity<T>(value: T): T {
return value;
}

const num = identity<number>(123); // number
const str = identity<string>("hello"); // string
const bool = identity<boolean>(true); // boolean

// Even simpler with type inference
const num2 = identity(123); // Inferred as number
const str2 = identity("hello"); // Inferred as string

// 2. Generic arrays
function getFirstElement<T>(arr: T[]): T | undefined {
return arr[0];
}

const firstNum = getFirstElement([1, 2, 3]); // number
const firstName = getFirstElement(["a", "b", "c"]); // string

// 3. Generic interfaces
interface Box<T> {
value: T;
}

const numberBox: Box<number> = { value: 123 };
const stringBox: Box<string> = { value: "hello" };

// 4. Generic classes
class Stack<T> {
private items: T[] = [];

push(item: T): void {
this.items.push(item);
}

pop(): T | undefined {
return this.items.pop();
}

peek(): T | undefined {
return this.items[this.items.length - 1];
}
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
numberStack.push(3);
console.log(numberStack.pop()); // 3

const stringStack = new Stack<string>();
stringStack.push("a");
stringStack.push("b");

// 5. Generic constraints
interface HasLength {
length: number;
}

function logLength<T extends HasLength>(item: T): void {
console.log(item.length);
}

logLength("hello"); // OK (string has length)
logLength([1, 2, 3]); // OK (array has length)
// logLength(123); // ❌ Error (number doesn't have length)

// 6. Multiple generic types
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}

const p1 = pair<string, number>("age", 25);
const p2 = pair("name", "John Kim"); // Type inference

// 7. Generic defaults
interface Container<T = string> {
value: T;
}

const container1: Container = { value: "hello" }; // string
const container2: Container<number> = { value: 123 };

Utility Types​

// TypeScript built-in utility types

// 1. Partial<T> (all properties optional)
interface User {
id: number;
name: string;
email: string;
}

function updateUser(id: number, updates: Partial<User>): void {
// updates can have only some properties
}

updateUser(1, { name: "John Kim" }); // OK
updateUser(2, { email: "new@example.com" }); // OK
updateUser(3, { name: "Jane Lee", email: "lee@example.com" }); // OK

// 2. Required<T> (all properties required)
interface PartialUser {
id?: number;
name?: string;
email?: string;
}

type RequiredUser = Required<PartialUser>;

const user: RequiredUser = {
id: 1,
name: "John Kim",
email: "kim@example.com"
// All properties required!
};

// 3. Readonly<T> (all properties readonly)
interface Config {
apiUrl: string;
timeout: number;
}

const config: Readonly<Config> = {
apiUrl: "https://api.example.com",
timeout: 5000
};

// ❌ Cannot modify
// config.apiUrl = "https://api2.example.com";

// 4. Pick<T, K> (select specific properties)
interface Product {
id: number;
name: string;
price: number;
description: string;
stock: number;
}

type ProductPreview = Pick<Product, "id" | "name" | "price">;

const preview: ProductPreview = {
id: 1,
name: "Laptop",
price: 1000000
// description, stock not needed
};

// 5. Omit<T, K> (exclude specific properties)
type ProductWithoutStock = Omit<Product, "stock">;

const product: ProductWithoutStock = {
id: 1,
name: "Laptop",
price: 1000000,
description: "High-performance laptop"
// stock excluded
};

// 6. Record<K, T> (key-value mapping)
type PageInfo = Record<string, { title: string; url: string }>;

const pages: PageInfo = {
home: { title: "Home", url: "/" },
about: { title: "About", url: "/about" },
contact: { title: "Contact", url: "/contact" }
};

// 7. Exclude<T, U> (exclude type from union)
type AllStatus = "pending" | "approved" | "rejected" | "draft";
type ActiveStatus = Exclude<AllStatus, "draft">;
// "pending" | "approved" | "rejected"

// 8. Extract<T, U> (extract type from union)
type Status = "pending" | "approved" | "rejected";
type PositiveStatus = Extract<Status, "approved">;
// "approved"

// 9. NonNullable<T> (remove null, undefined)
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>;
// string

// 10. ReturnType<T> (extract function return type)
function getUser() {
return {
id: 1,
name: "John Kim",
email: "kim@example.com"
};
}

type User = ReturnType<typeof getUser>;
// { id: number; name: string; email: string }

JavaScript β†’ TypeScript Conversion​

// JavaScript code
// user.js
function createUser(name, age, email) {
return {
name: name,
age: age,
email: email,
greet: function() {
return "Hello, " + this.name + "!";
}
};
}

function getUsers() {
return fetch('/api/users')
.then(res => res.json())
.then(data => data.users);
}

const user = createUser("John Kim", 25, "kim@example.com");
console.log(user.greet());

// ↓ Convert to TypeScript

// user.ts
interface User {
name: string;
age: number;
email: string;
greet(): string;
}

function createUser(name: string, age: number, email: string): User {
return {
name,
age,
email,
greet() {
return `Hello, ${this.name}!`;
}
};
}

interface ApiResponse {
users: User[];
}

async function getUsers(): Promise<User[]> {
const response = await fetch('/api/users');
const data: ApiResponse = await response.json();
return data.users;
}

const user: User = createUser("John Kim", 25, "kim@example.com");
console.log(user.greet());

// Advantages:
// 1. Type safety
// 2. IDE autocomplete
// 3. Easy refactoring
// 4. Prevent bugs in advance

TypeScript in React​

// React component (JavaScript)
// Button.jsx
function Button({ label, onClick, disabled }) {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
}

// ↓ Convert to TypeScript

// Button.tsx
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}

function Button({ label, onClick, disabled = false }: ButtonProps) {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
}

// Or use React.FC
const Button: React.FC<ButtonProps> = ({ label, onClick, disabled = false }) => {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
};

// Usage
<Button
label="Click"
onClick={() => console.log('clicked')}
disabled={false}
/>

// ❌ Wrong usage
<Button
label={123} // ❌ Error: label is string
onClick="handleClick" // ❌ Error: onClick is function
/>

// 2. State and Hooks
import { useState, useEffect } from 'react';

interface User {
id: number;
name: string;
email: string;
}

function UserProfile() {
// Specify state types
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
setLoading(true);
fetch('/api/user')
.then(res => res.json())
.then((data: User) => {
setUser(data);
setLoading(false);
})
.catch((err: Error) => {
setError(err.message);
setLoading(false);
});
}, []);

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return <div>No user</div>;

return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}

// 3. Event Handler
interface FormProps {
onSubmit: (data: { name: string; email: string }) => void;
}

function Form({ onSubmit }: FormProps) {
const [name, setName] = useState<string>('');
const [email, setEmail] = useState<string>('');

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit({ name, email });
};

const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
};

return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={handleNameChange}
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button type="submit">Submit</button>
</form>
);
}

Common Errors and Solutions​

// 1. Object is possibly 'null' or 'undefined'
// ❌ Error
function getLength(text: string | null) {
return text.length; // ❌ text might be null
}

// βœ… Solution 1: null check
function getLength(text: string | null) {
if (text === null) {
return 0;
}
return text.length; // βœ… OK
}

// βœ… Solution 2: Optional Chaining
function getLength(text: string | null) {
return text?.length ?? 0;
}

// 2. Type 'X' is not assignable to type 'Y'
// ❌ Error
interface User {
name: string;
age: number;
}

const user: User = {
name: "John Kim"
// age missing!
};

// βœ… Solution: Add all properties
const user: User = {
name: "John Kim",
age: 25
};

// 3. Cannot find name 'X'
// ❌ Error
console.log(process.env.API_URL); // ❌ No process type

// βœ… Solution: Install @types
// npm install --save-dev @types/node

// 4. Property 'X' does not exist on type 'Y'
// ❌ Error
const obj = {};
obj.name = "John Kim"; // ❌ No name property

// βœ… Solution 1: Type definition
interface Obj {
name: string;
}

const obj: Obj = { name: "" };
obj.name = "John Kim"; // βœ… OK

// βœ… Solution 2: Index signature
const obj: { [key: string]: string } = {};
obj.name = "John Kim"; // βœ… OK

// 5. Type assertions
const input = document.getElementById('myInput');
// input is HTMLElement | null

// ❌ Error
input.value = "hello"; // HTMLElement doesn't have value

// βœ… Solution: Type assertion
const input = document.getElementById('myInput') as HTMLInputElement;
input.value = "hello"; // βœ… OK

// Or
const input = <HTMLInputElement>document.getElementById('myInput');
input.value = "hello"; // βœ… OK

// 6. Non-null assertion operator (!)
const element = document.getElementById('app');
// element is HTMLElement | null

// ❌ Error
element.innerHTML = "hello"; // Might be null

// βœ… Solution: null check
if (element) {
element.innerHTML = "hello";
}

// Or ! operator (only when certain!)
element!.innerHTML = "hello"; // Assert not null

// 7. Type narrowing
function printValue(value: string | number) {
// ❌ Error
// console.log(value.toUpperCase()); // number doesn't have toUpperCase

// βœ… Solution: Type check with typeof
if (typeof value === "string") {
console.log(value.toUpperCase()); // βœ… OK (string)
} else {
console.log(value.toFixed(2)); // βœ… OK (number)
}
}

πŸ€” Frequently Asked Questions​

Q1. Why should we use TypeScript?​

A: There are several reasons:

// 1. Prevent bugs in advance
// JavaScript
function calculateTotal(items) {
let total = 0;
items.forEach(item => {
total += item.price;
});
return total;
}

// Bugs discovered during execution:
calculateTotal(null); // πŸ’₯ Runtime Error!
calculateTotal([{ name: "Product" }]); // πŸ’₯ price is undefined

// TypeScript
interface Item {
name: string;
price: number;
}

function calculateTotal(items: Item[]): number {
let total = 0;
items.forEach(item => {
total += item.price;
});
return total;
}

// Discovered during code writing:
calculateTotal(null); // ❌ Compile Error!
calculateTotal([{ name: "Product" }]); // ❌ Compile Error! (no price)

// 2. Autocomplete and IntelliSense
const user = {
name: "John Kim",
age: 25,
email: "kim@example.com"
};

// JavaScript: No autocomplete when typing user.
// TypeScript: Autocomplete for name, age, email when typing user.!

// 3. Safe refactoring
// When changing function signature
function greet(name) {
return `Hello, ${name}!`;
}

// TypeScript marks errors at all call sites
// JavaScript only finds problems when executed

// 4. Acts as documentation
// JavaScript
function createUser(name, age, email, isAdmin) {
// Don't know if name is string or number
// Don't know if age is required or optional
// Don't know what isAdmin is
}

// TypeScript
interface CreateUserParams {
name: string;
age: number;
email: string;
isAdmin?: boolean; // Optional, defaults to false
}

function createUser(params: CreateUserParams): User {
// Everything is clear!
}

// 5. Team collaboration
// Looking at TypeScript code:
// - What arguments the function receives
// - What type it returns
// - What properties exist
// Everything is clear!

// Conclusion:
// JavaScript: Fast prototyping, simple scripts
// TypeScript: Large-scale projects, team collaboration, maintenance

Q2. Doesn't TypeScript have any disadvantages?​

A: There are some disadvantages:

// Disadvantage 1: Learning curve
// Can start immediately knowing only JavaScript
// TypeScript requires learning the type system

// Disadvantage 2: Initial setup
// JavaScript: Create file and run immediately
// TypeScript: Config files, compilation needed

// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
// ... many options
}
}

// Disadvantage 3: Type writing time
// JavaScript
const users = [
{ name: "John Kim", age: 25 },
{ name: "Jane Lee", age: 30 }
];

// TypeScript (type definition needed)
interface User {
name: string;
age: number;
}

const users: User[] = [
{ name: "John Kim", age: 25 },
{ name: "Jane Lee", age: 30 }
];

// Disadvantage 4: Third-party libraries
// Libraries without type definitions require using any
import someLib from 'old-library'; // No types
// Need to install @types/old-library

// Disadvantage 5: Compilation time
// Large projects take compilation time
// (But greatly improved with esbuild, SWC)

// But advantages far outweigh disadvantages!
// Especially essential for large-scale projects!

Q3. Is it okay to use any?​

A: It's best to avoid it as much as possible:

// ❌ Overuse of any (loses TypeScript advantages)
function processData(data: any) {
return data.map((item: any) => {
return {
id: item.id,
value: item.value
};
});
}

// βœ… Use proper types
interface DataItem {
id: number;
value: string;
}

function processData(data: DataItem[]) {
return data.map(item => {
return {
id: item.id,
value: item.value
};
});
}

// When it's okay to use any:
// 1. Third-party libraries (no type definitions)
import oldLib from 'very-old-library';
const result: any = oldLib.doSomething();

// 2. JSON parsing (unknown structure)
const data: any = JSON.parse(jsonString);
// Then validate with type guards

// 3. Legacy code migration
// Gradual transition from JavaScript β†’ TypeScript

// Alternatives:
// - unknown: Safer than any
// - Generics: Preserve types
// - Type guards: Runtime validation

Q4. interface vs type, when to use which?​

A: It depends on the situation:

// Use Interface (recommended)
// 1. Object structure definition
interface User {
id: number;
name: string;
}

// 2. Extensible
interface Student extends User {
studentId: string;
}

// 3. Declaration Merging
interface Window {
myCustomProperty: string;
}

interface Window {
anotherProperty: number;
}
// Both declarations merged!

// Use Type
// 1. Union types
type Status = "pending" | "approved" | "rejected";

// 2. Intersection types
type Employee = Person & Worker;

// 3. Function types
type GreetFunction = (name: string) => string;

// 4. Utility types
type PartialUser = Partial<User>;
type PickedUser = Pick<User, "id" | "name">;

// 5. Tuples
type Point = [number, number];

// 6. Mapped Types
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};

// Recommendation:
// - Use interface by default
// - Use type when union/intersection needed
// - Maintain consistency (within project)

Q5. How do I start a TypeScript project?​

A: There are several ways:

# 1. Create React App (TypeScript template)
npx create-react-app my-app --template typescript

# 2. Next.js (TypeScript)
npx create-next-app@latest my-app --typescript

# 3. Vite (TypeScript)
npm create vite@latest my-app -- --template react-ts

# 4. Add TypeScript to existing project
npm install --save-dev typescript @types/react @types/react-dom

# Generate tsconfig.json
npx tsc --init

# 5. Pure TypeScript project
mkdir my-project
cd my-project
npm init -y
npm install --save-dev typescript @types/node

# Generate tsconfig.json
npx tsc --init

# Create src/index.ts file
echo 'console.log("Hello TypeScript!");' > src/index.ts

# Compile
npx tsc

# Execute
node dist/index.js
// tsconfig.json (recommended settings)
{
"compilerOptions": {
// Target JavaScript version
"target": "ES2020",

// Module system
"module": "ESNext",
"moduleResolution": "node",

// JSX support (React)
"jsx": "react-jsx",

// Strict type checking
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,

// Module resolution
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,

// Output directory
"outDir": "./dist",

// Source map generation
"sourceMap": true,

// Skip unnecessary checks
"skipLibCheck": true,

// Path aliases
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

πŸŽ“ Next Steps​

Once you understand TypeScript basics, try learning:

  1. What is React? (document in progress) - React development with TypeScript
  2. What is a Bundler? - TypeScript build environment
  3. What is TDD? (document in progress) - Writing tests with TypeScript

🎬 Conclusion​

TypeScript makes JavaScript safer and more productive:

  • Type Safety: Prevent bugs in advance
  • Autocomplete: Improve development speed
  • Refactoring: Safe code changes
  • Documentation: Code as documentation

TypeScript is essential for large-scale projects and team collaboration. There's a learning curve, but it's well worth the investment!