DEV Community

arenasbob2024-cell
arenasbob2024-cell

Posted on • Originally published at viadreams.cc

TypeScript Tips That Changed How I Write Code

TypeScript is more than adding : string to your variables. Here are the patterns that level up your TypeScript code.

1. Discriminated Unions

The most powerful pattern in TypeScript for handling different shapes of data:

type ApiResponse =
  | { status: 'success'; data: User[] }
  | { status: 'error'; message: string }
  | { status: 'loading' };

function handleResponse(response: ApiResponse) {
  switch (response.status) {
    case 'success':
      // TypeScript knows response.data exists here
      return response.data.map(u => u.name);
    case 'error':
      // TypeScript knows response.message exists here
      console.error(response.message);
      return [];
    case 'loading':
      return null;
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Type Guards That Actually Work

// Simple type guard
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

// Object type guard
function isUser(obj: unknown): obj is User {
  return (
    typeof obj === 'object' &&
    obj !== null &&
    'id' in obj &&
    'name' in obj
  );
}

// Usage
const data: unknown = await fetchData();
if (isUser(data)) {
  console.log(data.name); // TypeScript knows this is safe
}
Enter fullscreen mode Exit fullscreen mode

3. satisfies Keyword

Validate a type without widening it:

const config = {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retries: 3,
} satisfies Record<string, string | number>;

// config.apiUrl is still typed as string (not string | number)
config.apiUrl.toUpperCase(); // Works!
Enter fullscreen mode Exit fullscreen mode

4. Template Literal Types

type EventName = `on${Capitalize<'click' | 'focus' | 'blur'>}`;
// "onClick" | "onFocus" | "onBlur"

type CSSProperty = `${string}-${string}`;
// Matches "font-size", "margin-top", etc.
Enter fullscreen mode Exit fullscreen mode

5. Utility Types You Should Know

// Make all properties optional
type PartialUser = Partial<User>;

// Make all properties required
type RequiredUser = Required<User>;

// Pick specific properties
type UserPreview = Pick<User, 'id' | 'name'>;

// Omit specific properties
type UserWithoutPassword = Omit<User, 'password'>;

// Extract from union
type SuccessResponse = Extract<ApiResponse, { status: 'success' }>;
Enter fullscreen mode Exit fullscreen mode

6. const Assertions

// Without const assertion
const routes = ['/', '/about', '/blog']; // string[]

// With const assertion
const routes = ['/', '/about', '/blog'] as const; // readonly ['/', '/about', '/blog']

// Now you can use it as a type
type Route = typeof routes[number]; // '/' | '/about' | '/blog'
Enter fullscreen mode Exit fullscreen mode

7. Generic Constraints

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: 'Alice', age: 30 };
getProperty(user, 'name'); // string
getProperty(user, 'age');  // number
getProperty(user, 'foo');  // Error! 'foo' is not a key of user
Enter fullscreen mode Exit fullscreen mode

Learn More

200+ free developer tools at DevToolBox - JSON formatter, regex builder, diff tool, and more.

Top comments (0)