Referensi lengkap TypeScript. Types, Interfaces, Generics, Decorators, dan fitur TypeScript 5.x. Perfect buat yang mau type-safe JavaScript.
Panduan awal untuk memulai pengembangan dengan TypeScript sebagai bahasa yang lebih aman dari JavaScript.
Cara menginstall TypeScript compiler dan mengintegrasikannya dalam proyek.
# Install TypeScript
npm install -g typescript
# Install untuk project
npm install --save-dev typescript
# Install dengan pnpm
pnpm add -D typescript
# Cek version
tsc --versionKonfigurasi proyek TypeScript dengan file tsconfig.json dan struktur folder.
# Bikin tsconfig.json
tsc --init
# Compile TypeScript
tsc
# Watch mode
tsc --watch
# Compile specific file
tsc file.tsKonfigurasi dasar TypeScript compiler untuk proyek dengan pengaturan yang umum digunakan.
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}Tipe-tipe data fundamental dalam TypeScript untuk memberikan type safety.
Tipe-tipe data primitif seperti string, number, boolean, dan lainnya.
// String
let name: string = "Budi";
let greeting: string = `Hello, ${name}`;
// Number
let age: number = 25;
let price: number = 99.99;
let hex: number = 0xf00d;
let binary: number = 0b1010;
// Boolean
let isActive: boolean = true;
let isDone: boolean = false;
// Null & Undefined
let nothing: null = null;
let notDefined: undefined = undefined;
// Symbol
let sym: symbol = Symbol("key");
// BigInt
let big: bigint = 100n;Cara mendefinisikan array dengan tipe data tertentu dan berbagai fitur array TypeScript.
// Array dengan tipe tertentu
let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ["Budi", "Ani", "Cici"];
// Generic array syntax
let scores: Array<number> = [90, 85, 95];
// Readonly array
let readonly readonlyNumbers: readonly number[] = [1, 2, 3];
// Tuple (fixed length & types)
let person: [string, number] = ["Budi", 25];
let rgb: [number, number, number] = [255, 0, 0];
// Optional tuple elements
let optional: [string, number?] = ["test"];
// Rest in tuples
let mixed: [string, ...number[]] = ["first", 1, 2, 3];Cara mendefinisikan object dengan struktur yang ketat dan properti yang terdefinisi.
// Object type
let user: { name: string; age: number } = {
name: "Budi",
age: 25
};
// Optional properties
let person: { name: string; age?: number } = {
name: "Ani"
};
// Readonly properties
let config: { readonly apiKey: string } = {
apiKey: "abc123"
};
// config.apiKey = "new"; // Error!
// Index signatures
let scores: { [key: string]: number } = {
math: 90,
english: 85
};Tipe-tipe khusus untuk kasus ketika kita tidak tahu tipe data atau kondisi yang tidak mungkin.
// Any (hindari sebisa mungkin!)
let anything: any = "hello";
anything = 42;
anything = true;
// Unknown (lebih aman dari any)
let userInput: unknown = "hello";
// Perlu type checking dulu
if (typeof userInput === "string") {
let length = userInput.length; // OK
}
// Never (gak pernah return)
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
// Void (function tanpa return value)
function logMessage(message: string): void {
console.log(message);
}Cara membuat tipe kustom menggunakan type aliases dan interfaces untuk reusability.
Membuat alias untuk tipe kompleks agar lebih mudah digunakan dan dipahami.
// Simple type alias
type ID = string | number;
type Username = string;
// Object type alias
type User = {
id: ID;
name: string;
email: string;
age?: number;
};
// Function type alias
type AddFunction = (a: number, b: number) => number;
// Union type
type Status = "active" | "inactive" | "pending";
// Intersection type
type Admin = User & {
role: "admin";
permissions: string[];
};// Basic interface
interface Product {
id: number;
name: string;
price: number;
}
// Optional & readonly
interface User {
readonly id: number;
name: string;
email?: string;
}
// Method signatures
interface Calculator {
add(a: number, b: number): number;
subtract(a: number, b: number): number;
}
// Index signature
interface StringMap {
[key: string]: string;
}
// Extending interfaces
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
bark(): void;
}
// Multiple extends
interface SmartDog extends Dog, Calculator {
iq: number;
}// Type alias bisa union
type Pet = Dog | Cat;
// Interface bisa di-merge (declaration merging)
interface Window {
title: string;
}
interface Window {
version: number;
}
// Jadi satu interface dengan title & version
// Prefer interface untuk object shapes
// Prefer type untuk unions, primitives, tuples// Function declaration
function add(a: number, b: number): number {
return a + b;
}
// Function expression
const multiply = function(a: number, b: number): number {
return a * b;
};
// Arrow function
const divide = (a: number, b: number): number => a / b;
// Optional parameters
function greet(name: string, greeting?: string): string {
return `${greeting || "Hello"}, ${name}`;
}
// Default parameters
function createUser(name: string, role: string = "user"): User {
return { name, role };
}
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}// Overload signatures
function getValue(id: number): string;
function getValue(name: string): number;
// Implementation signature
function getValue(idOrName: number | string): string | number {
if (typeof idOrName === "number") {
return "User" + idOrName;
} else {
return idOrName.length;
}
}
let result1 = getValue(1); // string
let result2 = getValue("Budi"); // numberinterface User {
name: string;
greet(this: User): void;
}
const user: User = {
name: "Budi",
greet(this: User) {
console.log(`Hello, I'm ${this.name}`);
}
};// Basic generic
function identity<T>(arg: T): T {
return arg;
}
let result1 = identity<string>("hello");
let result2 = identity<number>(42);
let result3 = identity("hello"); // Type inference
// Multiple type parameters
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
let p = pair<string, number>("age", 25);
// Generic constraints
function getLength<T extends { length: number }>(arg: T): number {
return arg.length;
}
getLength("hello"); // OK
getLength([1, 2, 3]); // OK
// getLength(123); // Error!// Generic interface
interface Box<T> {
value: T;
}
let stringBox: Box<string> = { value: "hello" };
let numberBox: Box<number> = { value: 42 };
// Generic with default
interface Container<T = string> {
item: T;
}
let container1: Container = { item: "default" };
let container2: Container<number> = { item: 123 };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);
const stringStack = new Stack<string>();
stringStack.push("hello");// Extends constraint
function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
// Keyof constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let user = { name: "Budi", age: 25 };
let name = getProperty(user, "name"); // string
let age = getProperty(user, "age"); // number// Union type
let id: string | number;
id = "abc123"; // OK
id = 123; // OK
// Function dengan union parameter
function printId(id: string | number): void {
console.log(`ID: ${id}`);
}
// Type narrowing
function process(value: string | number): void {
if (typeof value === "string") {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}
// Union dengan literal types
type Status = "success" | "error" | "loading";
let status: Status = "success";// Intersection type (&)
interface Person {
name: string;
age: number;
}
interface Employee {
employeeId: number;
department: string;
}
type Staff = Person & Employee;
const staff: Staff = {
name: "Budi",
age: 30,
employeeId: 12345,
department: "IT"
};// Tagged union
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
}
}interface User {
id: number;
name: string;
email: string;
age: number;
}
// Partial<T> - semua property jadi optional
type PartialUser = Partial<User>;
// Required<T> - semua property jadi required
type RequiredUser = Required<PartialUser>;
// Readonly<T> - semua property jadi readonly
type ReadonlyUser = Readonly<User>;
// Pick<T, K> - pilih property tertentu
type UserPreview = Pick<User, "id" | "name">;
// Omit<T, K> - buang property tertentu
type UserWithoutAge = Omit<User, "age">;
// Record<K, T> - bikin object type dengan keys tertentu
type Roles = "admin" | "user" | "guest";
type Permissions = Record<Roles, string[]>;
// Exclude<T, U> - exclude types dari union
type T1 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
// Extract<T, U> - extract types dari union
type T2 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
// NonNullable<T> - buang null & undefined
type T3 = NonNullable<string | number | null | undefined>; // string | number
// ReturnType<T> - ambil return type dari function
type Func = () => string;
type ReturnValue = ReturnType<Func>; // string
// Parameters<T> - ambil parameter types
type Params = Parameters<(a: string, b: number) => void>; // [string, number]// Basic mapped type
type Optional<T> = {
[K in keyof T]?: T[K];
};
// Readonly mapper
type ReadonlyType<T> = {
readonly [K in keyof T]: T[K];
};
// Conditional mapping
type NullableProps<T> = {
[K in keyof T]: T[K] | null;
};
// Key remapping
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};// Basic conditional type
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Infer keyword
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// Distributive conditional types
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>; // string[] | number[]// String literal types
type EventName = "click" | "scroll" | "mousemove";
type HandlerName = `on${Capitalize<EventName>}`;
// "onClick" | "onScroll" | "onMousemove"
// More complex
type PropEventSource<T> = {
on<K extends string & keyof T>
(eventName: `${K}Changed`, callback: (newValue: T[K]) => void): void;
};class Person {
// Properties
name: string;
age: number;
// Constructor
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// Methods
greet(): string {
return `Hello, I'm ${this.name}`;
}
}
const person = new Person("Budi", 25);class BankAccount {
public accountNumber: string; // Bisa diakses dari mana aja
private balance: number; // Cuma di dalam class
protected owner: string; // Di dalam class & subclass
constructor(accountNumber: string, owner: string) {
this.accountNumber = accountNumber;
this.owner = owner;
this.balance = 0;
}
// Public method
public deposit(amount: number): void {
this.balance += amount;
}
// Private method
private validateAmount(amount: number): boolean {
return amount > 0;
}
// Protected method
protected getBalance(): number {
return this.balance;
}
}// Shorthand untuk properties di constructor
class User {
constructor(
public name: string,
private age: number,
readonly id: string
) {}
}
// Sama aja kayak:
class User {
public name: string;
private age: number;
readonly id: string;
constructor(name: string, age: number, id: string) {
this.name = name;
this.age = age;
this.id = id;
}
}abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("Moving...");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Woof!");
}
}
// const animal = new Animal(); // Error! Gak bisa instantiate abstract class
const dog = new Dog();class MathUtils {
static PI = 3.14159;
static calculateCircleArea(radius: number): number {
return this.PI * radius ** 2;
}
}
console.log(MathUtils.PI);
console.log(MathUtils.calculateCircleArea(5));// Enable decorators di tsconfig.json
// "experimentalDecorators": true
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class BugReport {
type = "report";
title: string;
constructor(t: string) {
this.title = t;
}
}function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with`, args);
const result = original.apply(this, args);
console.log(`Result:`, result);
return result;
};
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}function readonly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false
});
}
class Person {
@readonly
name: string = "Budi";
}// Named exports
export const PI = 3.14159;
export function add(a: number, b: number): number {
return a + b;
}
export class User {
name: string;
}
// Export existing
const secret = "abc123";
export { secret };
// Export dengan rename
export { secret as apiKey };
// Default export
export default class Database {
connect() {}
}// Named imports
import { PI, add, User } from "./math";
// Import dengan rename
import { secret as apiKey } from "./config";
// Import all
import * as MathUtils from "./math";
// Default import
import Database from "./database";
// Mixed
import Database, { PI, add } from "./module";
// Type-only imports (TypeScript 3.8+)
import type { User } from "./types";
import { type User, createUser } from "./user";function printValue(value: string | number): void {
if (typeof value === "string") {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}class Dog {
bark() { console.log("Woof!"); }
}
class Cat {
meow() { console.log("Meow!"); }
}
function makeSound(animal: Dog | Cat): void {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}interface Bird {
fly(): void;
}
interface Fish {
swim(): void;
}
function move(animal: Bird | Fish): void {
if ("fly" in animal) {
animal.fly();
} else {
animal.swim();
}
}function isString(value: any): value is string {
return typeof value === "string";
}
function process(value: string | number): void {
if (isString(value)) {
console.log(value.toUpperCase()); // value is string here
}
}// TypeScript 5.0
function createArray<const T>(items: readonly T[]): readonly T[] {
return items;
}
const arr = createArray([1, 2, 3]); // readonly [1, 2, 3]// TypeScript 4.9+
type RGB = [number, number, number];
const color = { r: 255, g: 0, b: 0 } satisfies RGB;
// Type checking tanpa mengubah inferred type// TypeScript 5.0
enum Status {
Active = "ACTIVE",
Inactive = "INACTIVE"
}
// Better type checking & performance// Explicit typing (good)
const name: string = "Budi";
const age: number = 25;
// Type inference (also good)
const name = "Budi"; // TypeScript tahu ini string
const age = 25; // TypeScript tahu ini number
// Return type annotations (recommended)
function add(a: number, b: number): number {
return a + b;
}// Bad
function process(data: any): any {
return data;
}
// Good - use generics
function process<T>(data: T): T {
return data;
}
// Good - use unknown kalo emang gak tau
function process(data: unknown): void {
if (typeof data === "string") {
console.log(data.toUpperCase());
}
}// tsconfig.json
{
"compilerOptions": {
"strict": true,
// atau individually:
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true
}
}Login atau daftar akun gratis untuk membaca cheat sheet ini.