Kembali ke Blog
Cara Belajar TypeScript untuk JavaScript Developer
TypeScript adalah superset JavaScript yang menambahkan static typing. Mari pelajari cara menggunakannya.
Apa itu TypeScript?
Keuntungan TypeScript
- Type safety (catch errors early)
- Better IDE support (autocomplete)
- Self-documenting code
- Refactoring lebih aman
- Better tooling
- Growing adoption
TypeScript vs JavaScript
// JavaScript
function add(a, b) {
return a + b;
}
add("1", 2); // "12" (string concatenation)
// TypeScript
function add(a: number, b: number): number {
return a + b;
}
add("1", 2); // Error: Argument of type 'string' not assignable
Setup
Install TypeScript
# Global installation
npm install -g typescript
# Project installation
npm init -y
npm install typescript --save-dev
# Initialize tsconfig
npx tsc --init
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Compile dan Run
# Compile
npx tsc
# Watch mode
npx tsc --watch
# Run with ts-node
npm install -g ts-node
ts-node src/index.ts
Basic Types
Primitive Types
// String
let nama: string = "Budi";
// Number
let umur: number = 25;
let harga: number = 99.99;
// Boolean
let aktif: boolean = true;
// Any (avoid if possible)
let data: any = "could be anything";
// Unknown (safer than any)
let input: unknown = "something";
// Void (function returns nothing)
function log(message: string): void {
console.log(message);
}
// Null dan Undefined
let nullable: null = null;
let undef: undefined = undefined;
Arrays
// Array of numbers
let numbers: number[] = [1, 2, 3];
// Alternative syntax
let strings: Array<string> = ["a", "b", "c"];
// Mixed array with tuple
let tuple: [string, number] = ["Budi", 25];
// Array of objects
let users: { name: string; age: number }[] = [
{ name: "Budi", age: 25 },
{ name: "Ani", age: 23 },
];
Objects
// Inline type
let user: { name: string; age: number } = {
name: "Budi",
age: 25,
};
// Optional property
let config: { host: string; port?: number } = {
host: "localhost",
};
// Readonly
let point: { readonly x: number; readonly y: number } = {
x: 10,
y: 20,
};
// point.x = 5; // Error: Cannot assign to 'x'
Type Aliases dan Interfaces
Type Alias
// Basic type alias
type ID = string | number;
// Object type alias
type User = {
id: ID;
name: string;
email: string;
age?: number;
};
// Union type
type Status = "pending" | "approved" | "rejected";
// Function type
type MathFunc = (a: number, b: number) => number;
const add: MathFunc = (a, b) => a + b;
Interface
interface User {
id: number;
name: string;
email: string;
}
// Extend interface
interface Admin extends User {
role: string;
permissions: string[];
}
// Implement interface
class AdminUser implements Admin {
id: number;
name: string;
email: string;
role: string;
permissions: string[];
constructor(data: Admin) {
this.id = data.id;
this.name = data.name;
this.email = data.email;
this.role = data.role;
this.permissions = data.permissions;
}
}
Type vs Interface
// Interface: can be extended/merged
interface User {
name: string;
}
interface User {
age: number;
}
// User now has both name and age
// Type: can use union/intersection
type StringOrNumber = string | number;
type Combined = User & { email: string };
// Recommendation:
// - Use interface untuk objects
// - Use type untuk unions, primitives, tuples
Functions
Function Types
// Parameter dan return types
function greet(name: string): string {
return `Hello, ${name}!`;
}
// Arrow function
const add = (a: number, b: number): number => a + b;
// Optional parameters
function createUser(name: string, age?: number): User {
return { name, age: age || 0 };
}
// Default parameters
function greet(name: string = "World"): string {
return `Hello, ${name}!`;
}
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}
Function Overloading
function format(value: string): string;
function format(value: number): string;
function format(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase();
}
return value.toFixed(2);
}
console.log(format("hello")); // "HELLO"
console.log(format(3.14159)); // "3.14"
Generics
Basic Generics
// Generic function
function identity<T>(arg: T): T {
return arg;
}
const str = identity<string>("hello");
const num = identity<number>(42);
const inferred = identity("hello"); // Type inferred
// Generic array
function firstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
Generic Interfaces
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
interface User {
id: number;
name: string;
}
const userResponse: ApiResponse<User> = {
data: { id: 1, name: "Budi" },
status: 200,
message: "Success",
};
Generic Constraints
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): number {
console.log(arg.length);
return arg.length;
}
logLength("hello"); // 5
logLength([1, 2, 3]); // 3
// logLength(123); // Error: number doesn't have length
Utility Types
Built-in Utility Types
interface User {
id: number;
name: string;
email: string;
password: string;
}
// Partial - all properties optional
type PartialUser = Partial<User>;
// Required - all properties required
type RequiredUser = Required<PartialUser>;
// Pick - select properties
type UserPreview = Pick<User, "id" | "name">;
// Omit - exclude properties
type UserWithoutPassword = Omit<User, "password">;
// Readonly - all properties readonly
type ReadonlyUser = Readonly<User>;
// Record - create object type
type UserRoles = Record<string, string[]>;
const roles: UserRoles = {
admin: ["read", "write", "delete"],
user: ["read"],
};
Conditional Types
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"
// Extract dan Exclude
type T1 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T2 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
Classes
Class with Types
class User {
// Properties
public id: number;
public name: string;
private password: string;
protected email: string;
readonly createdAt: Date;
constructor(id: number, name: string, password: string) {
this.id = id;
this.name = name;
this.password = password;
this.email = "";
this.createdAt = new Date();
}
// Method
greet(): string {
return `Hello, ${this.name}!`;
}
// Getter
get displayName(): string {
return this.name.toUpperCase();
}
// Setter
set displayName(value: string) {
this.name = value;
}
}
Abstract Classes
abstract class Animal {
constructor(public name: string) {}
abstract makeSound(): string;
move(): void {
console.log(`${this.name} is moving`);
}
}
class Dog extends Animal {
makeSound(): string {
return "Woof!";
}
}
Type Guards
Type Narrowing
function printValue(value: string | number) {
if (typeof value === "string") {
console.log(value.toUpperCase()); // TypeScript knows it's string
} else {
console.log(value.toFixed(2)); // TypeScript knows it's number
}
}
// instanceof
class Dog {
bark() {
console.log("Woof!");
}
}
class Cat {
meow() {
console.log("Meow!");
}
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
// Custom type guard
interface Fish {
swim(): void;
}
interface Bird {
fly(): void;
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
Modules
Export dan Import
// user.ts
export interface User {
id: number;
name: string;
}
export function createUser(name: string): User {
return { id: Date.now(), name };
}
export default class UserService {
// ...
}
// main.ts
import UserService, { User, createUser } from "./user";
const user: User = createUser("Budi");
Type-only Imports
import type { User } from "./user";
import { type User, createUser } from "./user";
Error Handling
Typed Errors
class AppError extends Error {
constructor(message: string, public code: string, public statusCode: number) {
super(message);
this.name = "AppError";
}
}
function fetchUser(id: number): User {
if (id < 0) {
throw new AppError("Invalid ID", "INVALID_ID", 400);
}
// ...
}
try {
fetchUser(-1);
} catch (error) {
if (error instanceof AppError) {
console.log(error.code, error.statusCode);
}
}
Best Practices
Do’s
// Use strict mode
"strict": true
// Prefer interfaces for objects
interface User { ... }
// Use const assertions
const colors = ["red", "green", "blue"] as const;
// Use unknown instead of any
function parse(input: unknown) { ... }
// Explicit return types for public APIs
export function getUser(id: number): User { ... }
Don’ts
// Avoid any
let data: any; // ❌
// Avoid type assertions when possible
const user = data as User; // ❌ if avoidable
// Avoid non-null assertions
user!.name; // ❌ prefer optional chaining
// Don't ignore errors
// @ts-ignore // ❌
Kesimpulan
TypeScript meningkatkan developer experience dengan type safety dan better tooling. Start dengan basics, gradually add more advanced types seiring kebutuhan.
Ditulis oleh
Hendra Wijaya
Artikel Sebelumnya
Cara Belajar Python Programming untuk Pemula
Artikel Selanjutnya
Cara Cek Website Mobile Friendly untuk SEO