Loading...
Loading...
Loading...
Modern JS (ES6+) and TypeScript examples with best practices—destructuring, spread/rest, optional chaining, async/await, types, generics, and React typing.
// Destructuring – avoid repetitive .property access
const user = { id: 1, name: 'Jane', role: 'admin' };
// Object
const { name, role } = user;
const { name: userName, role = 'viewer' } = user; // rename + default
// Array
const [first, second, ...rest] = [1, 2, 3, 4];
// Nested
const { address: { city } } = user;
// In function params (best practice: only destructure what you use)
function greet({ name, role }: User) {
return `${name} (${role})`;
}// Spread (...) – copy/merge arrays and objects (shallow)
const a = [1, 2, 3];
const b = [...a, 4, 5]; // [1, 2, 3, 4, 5]
const user = { name: 'Jane', role: 'admin' };
const updated = { ...user, role: 'viewer' }; // override
// Rest – collect remaining items
const [first, ...rest] = [1, 2, 3, 4]; // rest = [2, 3, 4]
function sum(...nums: number[]) {
return nums.reduce((a, b) => a + b, 0);
}// Optional chaining (?.) – safe access to nested properties
const user = { profile: { avatar: { url: '...' } } };
// Instead of: user && user.profile && user.profile.avatar?.url
const url = user?.profile?.avatar?.url; // undefined if any step is null/undefined
// With arrays and optional call
const firstItem = items?.[0];
const result = obj.method?.(); // only call if method exists
// Nullish coalescing (??) – default only for null/undefined (not 0 or '')
const count = value ?? 10;
const name = input ?? 'Anonymous';// Template literals – multiline and interpolation
const name = 'Jane';
const greeting = `Hello, ${name}!`;
// Multiline (no need for \n)
const html = `
<div class="card">
<h2>${title}</h2>
</div>
`;
// Tagged template (custom processing)
function sql(strings: TemplateStringsArray, ...values: unknown[]) {
return strings.reduce((acc, s, i) => acc + s + (values[i] ?? ''), '');
}// Array methods – prefer immutable patterns
const users = [{ id: 1, name: 'Jane' }, { id: 2, name: 'John' }];
// map – transform each item
const names = users.map((u) => u.name);
// filter – keep items that pass a test
const active = users.filter((u) => u.id > 1);
// reduce – single value from array
const total = [1, 2, 3].reduce((acc, n) => acc + n, 0);
// find / findIndex
const user = users.find((u) => u.id === 2);
const idx = users.findIndex((u) => u.name === 'Jane');
// some / every
const hasAdmin = users.some((u) => u.role === 'admin');
const allValid = users.every((u) => u.id > 0);// ES modules – import/export
// lib/utils.ts
export function formatDate(d: Date) {
return d.toISOString().slice(0, 10);
}
export const APP_NAME = 'MyApp';
export default function greet(name: string) {
return `Hello, ${name}`;
}
// app.ts
import greet, { formatDate, APP_NAME } from './lib/utils';
import * as utils from './lib/utils';
import type { User } from './types'; // type-only import// async/await – prefer over raw .then() for readability
async function fetchUser(id) {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error('Failed to fetch');
return res.json();
}
// Parallel requests (best practice: use Promise.all for independent calls)
const [user, posts] = await Promise.all([
fetchUser(userId),
fetchPosts(userId),
]);
// Sequential when order matters
const user = await fetchUser(id);
const posts = await fetchPosts(user.id);// Error handling – try/catch and custom errors
async function fetchUser(id: string) {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
} catch (e) {
if (e instanceof Error) console.error(e.message);
throw e;
}
}
// Custom error class
class ApiError extends Error {
constructor(public code: number, message: string) {
super(message);
this.name = 'ApiError';
}
}// Closures – function retains access to outer scope
function createCounter(initial = 0) {
let count = initial;
return {
get() {
return count;
},
inc() {
count += 1;
return count;
},
};
}
const counter = createCounter(10);
counter.inc(); // 11
counter.get(); // 11// Prefer `interface` for object shapes; `type` for unions/intersections
interface User {
id: string;
name: string;
email?: string;
}
type Status = 'idle' | 'loading' | 'success' | 'error';
type ApiResponse<T> = { data: T } | { error: string };
// Utility types – reuse and transform
type ReadonlyUser = Readonly<User>;
type PartialUser = Partial<User>;
type UserPreview = Pick<User, 'id' | 'name'>;
type UserWithoutEmail = Omit<User, 'email'>;
// Generic with constraint
function getById<T extends { id: string }>(items: T[], id: string): T | undefined {
return items.find((item) => item.id === id);
}// Enable strict mode in tsconfig.json: "strict": true
// Avoid `any` – use `unknown` when type is truly unknown, then narrow
function parsePayload(data: unknown): User {
if (
typeof data === 'object' &&
data !== null &&
'id' in data &&
'name' in data
) {
return data as User;
}
throw new Error('Invalid payload');
}
// Narrow with type guards
function isUser(value: unknown): value is User {
return typeof value === 'object' && value !== null && 'id' in value;
}// Generics – reusable types and functions
function identity<T>(x: T): T {
return x;
}
// Generic constraint
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// Generic interface
interface Result<T, E = Error> {
ok: true;
value: T;
} | {
ok: false;
error: E;
}// Discriminated unions – narrow by a common field
type Success = { kind: 'success'; data: string };
type Failure = { kind: 'error'; message: string };
type Result = Success | Failure;
function handle(r: Result) {
if (r.kind === 'success') {
console.log(r.data);
} else {
console.error(r.message);
}
}// Mapped types & keyof – derive types from object shape
interface User {
id: string;
name: string;
email: string;
}
type UserKeys = keyof User; // 'id' | 'name' | 'email'
type OptionalUser = { [K in keyof User]?: User[K] };
type ReadonlyUser = { readonly [K in keyof User]: User[K] };// const assertions – narrow to literal types
const config = {
theme: 'dark',
count: 42,
} as const;
// config.theme is 'dark', config.count is 42
const colors = ['red', 'green', 'blue'] as const;
type Color = typeof colors[number]; // 'red' | 'green' | 'blue'// Typing React components and hooks
import { ReactNode, FormEvent } from 'react';
interface ButtonProps {
children: ReactNode;
onClick?: () => void;
disabled?: boolean;
type?: 'button' | 'submit';
}
function Button({ children, onClick, disabled = false, type = 'button' }: ButtonProps) {
return (
<button type={type} onClick={onClick} disabled={disabled}>
{children}
</button>
);
}
// Event handlers
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
// ...
};