🗂️ Typescript Cheat Sheet
A collection of useful TypeScript utilities and best practices for safer, more expressive code.
Function Overload
In Next.js
, you can call server functions from the client side directly. However, type hints may be misleading and cause errors if not handled carefully.
For example:
type FormData = {
name: string
age: string
}
export function serverAction(data: FormData) {
const { name, age } = data
}
Problem: You cannot trust what the client submits—the
name
orage
property may not exist in reality. Always validate incoming data on the server.
A safer approach:
function validate(data: unknown): FormData {
// validate data here (e.g., using zod, yup, or manual checks)
...
return data as FormData
}
export function serverAction(data: FormData) {
const validData = validate(data)
const { name, age } = data // Oops, should use validData here!
}
Pitfall: Declaring
data
asFormData
is not ideal, since TypeScript will not warn you if you accidentally usedata.name
ordata.age
before validation. You should change its type tounknown
:
export function serverAction(data: unknown) {
const validData = validate(data)
// const { name, age } = data // TypeScript error, as expected
const { name, age } = validData
}
However, this approach loses type hints on the client side.
Solution: Use function overloads to provide type hints for the client, while keeping runtime safety on the server:
type FormData = {
name: string
age: string
}
// Overload for client-side type hints
export function serverAction(data: FormData): void
// Implementation for server-side safety
export function serverAction(data: unknown) {
const validData = validate(data)
const { name, age } = validData
}
function validate(data: unknown): FormData {
// validate data here
...
return data as FormData
}
Tip: Use runtime validation libraries like zod, yup, or io-ts for robust type-safe validation.
DistributiveOmit
TypeScript's built-in Omit
does not distribute over union types as you might expect.
Use DistributiveOmit
to omit keys from each member of a union:
type DistributiveOmit<T, K extends keyof any> = T extends unknown
? Omit<T, K>
: never
Example:
interface A {
id: string
label: string
a: string
}
interface B {
id: string
label: string
b: string
}
type C = A | B
// This does NOT work as expected; it behaves like Omit<A, 'id'>
type D = Omit<C, 'id'>
// Use DistributiveOmit for correct behavior:
type E = DistributiveOmit<C, 'id'>
ArrayElement
Extract the element type from an array or tuple:
type ArrayElement<T> = T extends readonly (infer E)[] ? E : never
Example:
const chars = ['a', 'b', 'c'] as const
type Char = ArrayElement<typeof chars> // 'a' | 'b' | 'c'
// Or, using TypeScript's built-in indexed access:
const char: (typeof chars)[number] = 'a'
Extend Third-party Module Interface
To extend or augment types from a third-party module, use module augmentation:
import 'module-name'
declare module 'module-name' {
export interface ToBeExtended {
prop: unknown
}
}
Tip: This is useful for adding custom properties to types from libraries like Express, React, or Next.js.
Utility Types
Some other handy utility types:
Partial<T>
: Makes all properties inT
optional.Required<T>
: Makes all properties inT
required.Readonly<T>
: Makes all properties inT
readonly.Record<K, T>
: Constructs an object type with keys of typeK
and values of typeT
.Pick<T, K>
: Picks a set of properties fromT
.Exclude<T, U>
: Excludes fromT
those types that are assignable toU
.Extract<T, U>
: Extracts fromT
those types that are assignable toU
.
Type Guards
Type guards help you narrow types at runtime:
function isString(value: unknown): value is string {
return typeof value === 'string'
}
Example:
function printLength(value: unknown) {
if (isString(value)) {
console.log(value.length)
}
}
Type Inference with as const
Use as const
to infer literal types:
const colors = ['red', 'green', 'blue'] as const
type Color = (typeof colors)[number] // 'red' | 'green' | 'blue'
Resources
- TypeScript Handbook
- Type Challenges
- zod - TypeScript-first schema validation
- ts-toolbelt - Advanced TypeScript types
TypeScript is a powerful tool for writing safer, more expressive JavaScript. Use these patterns and utilities to write robust, maintainable code!