Last Updated: 3/9/2026
TypeScript Usage
Nano ID works seamlessly with TypeScript and supports advanced type patterns.
Basic Usage
Nano ID works out of the box with TypeScript:
import { nanoid } from 'nanoid'
const id: string = nanoid()
// Type: stringNo additional type definitions needed - types are included in the package.
Type Definitions
nanoid()
function nanoid(size?: number): stringcustomAlphabet()
function customAlphabet(
alphabet: string,
defaultSize: number
): (size?: number) => stringcustomRandom()
function customRandom(
alphabet: string,
size: number,
random: (bytes: number) => Uint8Array
): () => stringOpaque Types (Branded Types)
Create type-safe ID types that can’t be accidentally mixed:
import { nanoid } from 'nanoid'
// Declare unique brands
declare const userIdBrand: unique symbol
declare const postIdBrand: unique symbol
// Create branded types
type UserId = string & { [userIdBrand]: true }
type PostId = string & { [postIdBrand]: true }
// Type-safe ID generation
function createUserId(): UserId {
return nanoid() as UserId
}
function createPostId(): PostId {
return nanoid() as PostId
}
// Usage
const userId = createUserId()
const postId = createPostId()
// Type checking prevents mixing
function getUser(id: UserId) { /* ... */ }
function getPost(id: PostId) { /* ... */ }
getUser(userId) // ✅ OK
getPost(postId) // ✅ OK
getUser(postId) // ❌ Type error!
getPost(userId) // ❌ Type error!Benefits
✅ Compile-time safety - Can’t pass wrong ID type ✅ Self-documenting - Function signatures show expected ID type ✅ Refactoring safety - TypeScript catches ID type mismatches
Generic Type Parameters
Nano ID supports explicit type casting:
import { nanoid } from 'nanoid'
declare const userIdBrand: unique symbol
type UserId = string & { [userIdBrand]: true }
// Explicit type parameter
const id = nanoid<UserId>()
// Type: UserId
// Or use type assertion
const id2: UserId = nanoid()
// Also worksInterfaces and Types
Database Models
import { nanoid } from 'nanoid'
interface User {
id: string
email: string
createdAt: number
}
function createUser(email: string): User {
return {
id: nanoid(),
email,
createdAt: Date.now()
}
}With Branded Types
import { nanoid } from 'nanoid'
declare const userIdBrand: unique symbol
type UserId = string & { [userIdBrand]: true }
interface User {
id: UserId
email: string
createdAt: number
}
function createUser(email: string): User {
return {
id: nanoid() as UserId, // Cast to branded type
email,
createdAt: Date.now()
}
}
// Type-safe lookups
function findUser(id: UserId): Promise<User | null> {
return db.users.findOne({ id })
}React with TypeScript
Component Props
import { nanoid } from 'nanoid'
import { useState } from 'react'
interface Todo {
id: string
text: string
completed: boolean
}
interface TodoListProps {
initialTodos?: Todo[]
}
function TodoList({ initialTodos = [] }: TodoListProps) {
const [todos, setTodos] = useState<Todo[]>(initialTodos)
const addTodo = (text: string) => {
const newTodo: Todo = {
id: nanoid(),
text,
completed: false
}
setTodos([...todos, newTodo])
}
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
)
}With Branded Types
import { nanoid } from 'nanoid'
import { useState } from 'react'
declare const todoIdBrand: unique symbol
type TodoId = string & { [todoIdBrand]: true }
interface Todo {
id: TodoId
text: string
completed: boolean
}
function TodoList() {
const [todos, setTodos] = useState<Todo[]>([])
const addTodo = (text: string) => {
const newTodo: Todo = {
id: nanoid() as TodoId,
text,
completed: false
}
setTodos([...todos, newTodo])
}
const toggleTodo = (id: TodoId) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
))
}
return (
<ul>
{todos.map(todo => (
<li key={todo.id} onClick={() => toggleTodo(todo.id)}>
{todo.text}
</li>
))}
</ul>
)
}Custom Alphabet with Types
import { customAlphabet } from 'nanoid'
// Type-safe custom alphabet
const nanoidHex = customAlphabet('0123456789abcdef', 16)
const id: string = nanoidHex()
// Type: string
// With branded type
declare const hexIdBrand: unique symbol
type HexId = string & { [hexIdBrand]: true }
function createHexId(): HexId {
return nanoidHex() as HexId
}API Responses
Type-Safe API Models
import { nanoid } from 'nanoid'
declare const userIdBrand: unique symbol
declare const sessionIdBrand: unique symbol
type UserId = string & { [userIdBrand]: true }
type SessionId = string & { [sessionIdBrand]: true }
interface LoginResponse {
userId: UserId
sessionId: SessionId
expiresAt: number
}
function createSession(userId: UserId): LoginResponse {
return {
userId,
sessionId: nanoid() as SessionId,
expiresAt: Date.now() + 3600000
}
}
// Type-safe session validation
function validateSession(sessionId: SessionId): boolean {
return sessions.has(sessionId)
}Utility Types
ID Factory
import { nanoid } from 'nanoid'
// Generic ID factory
function createIdFactory<T extends string>() {
return (): T => nanoid() as T
}
// Create specific factories
declare const userIdBrand: unique symbol
declare const postIdBrand: unique symbol
type UserId = string & { [userIdBrand]: true }
type PostId = string & { [postIdBrand]: true }
const createUserId = createIdFactory<UserId>()
const createPostId = createIdFactory<PostId>()
const userId = createUserId() // Type: UserId
const postId = createPostId() // Type: PostIdEntity Base Type
import { nanoid } from 'nanoid'
declare const entityIdBrand: unique symbol
type EntityId = string & { [entityIdBrand]: true }
interface Entity {
id: EntityId
createdAt: number
updatedAt: number
}
interface User extends Entity {
email: string
name: string
}
interface Post extends Entity {
title: string
content: string
authorId: EntityId
}
function createEntity<T extends Omit<Entity, 'id' | 'createdAt' | 'updatedAt'>>(
data: T
): T & Entity {
const now = Date.now()
return {
...data,
id: nanoid() as EntityId,
createdAt: now,
updatedAt: now
}
}
const user = createEntity<Omit<User, keyof Entity>>({
email: 'user@example.com',
name: 'Alice'
})
// Type: User (with id, createdAt, updatedAt)Strict Null Checks
Nano ID always returns a string, never null/undefined:
import { nanoid } from 'nanoid'
// With strict null checks enabled
const id = nanoid()
// Type: string (never null | undefined)
// No need for null checks
function saveId(id: string) {
localStorage.setItem('id', id) // ✅ Safe
}
saveId(nanoid()) // ✅ Always worksType Guards
Validate ID format at runtime:
import { nanoid } from 'nanoid'
declare const userIdBrand: unique symbol
type UserId = string & { [userIdBrand]: true }
// Type guard for branded IDs
function isUserId(value: unknown): value is UserId {
return (
typeof value === 'string' &&
value.length === 21 &&
/^[A-Za-z0-9_-]+$/.test(value)
)
}
// Usage
function getUser(id: unknown) {
if (!isUserId(id)) {
throw new Error('Invalid user ID')
}
// Type is now UserId
return db.users.findOne({ id })
}Discriminated Unions
Type-safe entity handling:
import { nanoid } from 'nanoid'
type EntityType = 'user' | 'post' | 'comment'
interface BaseEntity {
id: string
type: EntityType
}
interface User extends BaseEntity {
type: 'user'
email: string
}
interface Post extends BaseEntity {
type: 'post'
title: string
}
interface Comment extends BaseEntity {
type: 'comment'
text: string
}
type Entity = User | Post | Comment
function createEntity<T extends EntityType>(
type: T,
data: Omit<Extract<Entity, { type: T }>, 'id' | 'type'>
): Extract<Entity, { type: T }> {
return {
id: nanoid(),
type,
...data
} as Extract<Entity, { type: T }>
}
const user = createEntity('user', { email: 'user@example.com' })
// Type: User
const post = createEntity('post', { title: 'Hello' })
// Type: PostBest Practices
✅ Do
// Use branded types for type safety
declare const userIdBrand: unique symbol
type UserId = string & { [userIdBrand]: true }
// Create factory functions
function createUserId(): UserId {
return nanoid() as UserId
}
// Use interfaces for entities
interface User {
id: UserId
email: string
}❌ Don’t
// Don't use 'any'
const id: any = nanoid() // ❌ Loses type safety
// Don't mix ID types without branded types
function getUser(id: string) { } // ❌ Accepts any string
function getPost(id: string) { } // ❌ Can't distinguish from user ID
// Don't forget to cast when using branded types
const user: User = {
id: nanoid(), // ❌ Type error if User.id is UserId
email: 'user@example.com'
}Common Patterns
Repository Pattern
import { nanoid } from 'nanoid'
declare const userIdBrand: unique symbol
type UserId = string & { [userIdBrand]: true }
interface User {
id: UserId
email: string
name: string
}
class UserRepository {
async create(data: Omit<User, 'id'>): Promise<User> {
const user: User = {
id: nanoid() as UserId,
...data
}
await db.users.insert(user)
return user
}
async findById(id: UserId): Promise<User | null> {
return db.users.findOne({ id })
}
async update(id: UserId, data: Partial<Omit<User, 'id'>>): Promise<User> {
await db.users.update({ id }, data)
return this.findById(id) as Promise<User>
}
}Related
- React Integration - React-specific patterns
- Core API - API reference
- Quick Start Guide - Basic usage