TypeScript Types Reference
Overview
CS2Inspect uses a comprehensive TypeScript type system to ensure type safety throughout the application. This document provides reference documentation for all type definitions, branded types, type guards, and utilities.
Type System Architecture
The type system is organized into several categories:
types/
├── core/ # Core types (branded types, common utilities)
├── api/ # API request/response types
├── business/ # Business logic types (items, configurations)
├── database/ # Database record types
├── components/ # Component-specific types
└── index.ts # Central export
server/types/
├── api.ts # Server API types
├── database.ts # Server database types
├── items.ts # Enhanced item types
├── inspect.ts # Inspect system types
├── health.ts # Health check types
└── index.ts # Server types exportBranded Types
Branded types add compile-time safety by preventing accidental mixing of different ID types.
Location: /types/core/branded.ts
Entity IDs
LoadoutId
type LoadoutId = number & { readonly __brand: 'LoadoutId' }Loadout identifier (numeric in database, often string in URLs).
Usage:
import { toLoadoutId } from '~/types/core/branded'
// Convert from URL param
const loadoutId = toLoadoutId(params.id)
// Type-safe database query
await db.select().from(loadouts).where(eq(loadouts.id, loadoutId))Type Guard:
function isValidLoadoutId(value: unknown): value is LoadoutId {
return typeof value === 'number' && Number.isInteger(value) && value > 0
}SteamId
type SteamId = string & { readonly __brand: 'SteamId' }64-bit Steam identifier (17-digit string).
Example: "76561198012345678"
Usage:
import { toSteamId } from '~/types/core/branded'
const steamId = toSteamId(user.steamId)
await loadoutStore.fetchLoadouts(steamId)Type Guard:
function isValidSteamId(value: unknown): value is SteamId {
return typeof value === 'string' && /^\d{17}$/.test(value)
}Item Identifiers
Defindex
type Defindex = number & { readonly __brand: 'Defindex' }Weapon/item definition index (unique identifier for each item type).
Examples:
7- AK-47500- Bayonet5027- Hand Wraps
Usage:
import { toDefindex } from '~/types/core/branded'
const weaponDefindex = toDefindex(7) // AK-47PaintIndex
type PaintIndex = number & { readonly __brand: 'PaintIndex' }Paint/skin index (unique identifier for each skin).
Examples:
12- Crimson Web44- Asiimov253- Fire Serpent
Usage:
import { toPaintIndex } from '~/types/core/branded'
const skinIndex = toPaintIndex(253) // Fire SerpentPaintSeed
type PaintSeed = number & { readonly __brand: 'PaintSeed' }Pattern seed (determines pattern variation).
Range: 0-999
Examples:
661- Blue Gem Case Hardened0- Default pattern
Usage:
import { toPaintSeed } from '~/types/core/branded'
const seed = toPaintSeed(661) // Blue Gem seedOther Item IDs
type StickerId = number & { readonly __brand: 'StickerId' }
type KeychainId = number & { readonly __brand: 'KeychainId' }
type MusicKitDefindex = number & { readonly __brand: 'MusicKitDefindex' }
type PinDefindex = number & { readonly __brand: 'PinDefindex' }Converters:
import {
toStickerId,
toKeychainId,
toMusicKitDefindex,
toPinDefindex
} from '~/types/core/branded'Admin & Utility IDs
type AdminId = number & { readonly __brand: 'AdminId' }
type BanId = number & { readonly __brand: 'BanId' }
type SettingKey = string & { readonly __brand: 'SettingKey' }
type ISOTimestamp = string & { readonly __brand: 'ISOTimestamp' }Converters:
import {
toAdminId,
toBanId,
toSettingKey,
toISOTimestamp
} from '~/types/core/branded'Type Guards: isValidAdminId(), isValidBanId(), isValidSettingKey(), isValidISOTimestamp()
Item Configuration Types
Location: /types/business/items.ts
Base Configuration
interface ItemConfiguration {
active: boolean
team: number // 1=T, 2=CT
defindex: number
paintIndex: number
paintIndexOverride: boolean
pattern: number // 0-999
wear: number // 0.0-1.0
}Weapon Configuration
interface WeaponConfiguration extends ItemConfiguration {
statTrak: boolean
statTrakCount: number
nameTag: string
stickers: Array<StickerConfig | null> // 5 slots
keychain: KeychainConfig | null
}
interface StickerConfig {
id: number
wear: number // 0.0-1.0
scale: number // Scale factor
rotation: number // Rotation in degrees
x: number // X offset
y: number // Y offset
}
interface KeychainConfig {
id: number
seed: number
x: number
y: number
z: number
}Knife Configuration
interface KnifeConfiguration extends ItemConfiguration {
statTrak: boolean
statTrakCount: number
nameTag: string
}Glove Configuration
interface GloveConfiguration extends ItemConfiguration {
// No additional properties beyond ItemConfiguration
}Type Guards
function isWeaponConfiguration(config: ItemConfiguration): config is WeaponConfiguration
function isKnifeConfiguration(config: ItemConfiguration): config is KnifeConfiguration
function isGloveConfiguration(config: ItemConfiguration): config is GloveConfigurationUsage:
import { isWeaponConfiguration } from '~/types/business/items'
if (isWeaponConfiguration(config)) {
// TypeScript knows config has stickers, keychain, etc.
console.log(config.stickers)
console.log(config.nameTag)
}Database Record Types
Location: /types/database/records.ts
DBLoadout
interface DBLoadout {
id: number
steamid: string
name: string
selected_knife_t: number | null
selected_knife_ct: number | null
selected_glove_t: number | null
selected_glove_ct: number | null
selected_agent_ct: number | null
selected_agent_t: number | null
selected_music: number | null
active: boolean | number
is_default: boolean | number
created_at: string
updated_at: string
}DBWeapon
interface DBWeapon {
id: number
steamid: string
loadoutid: number
team: number
weapon_defindex: number
weapon_name: string
active: boolean | number
paintindex: number
paintseed: number
paintwear: number
stattrak_enabled: boolean | number
stattrak_count: number
nametag: string | null
created_at: string
updated_at: string
}DBKnife
interface DBKnife {
id: number
steamid: string
loadoutid: number
team: number
weapon_defindex: number
weapon_name: string
active: boolean | number
paintindex: number
paintseed: number
paintwear: number
stattrak_enabled: boolean | number
stattrak_count: number
nametag: string | null
created_at: string
updated_at: string
}DBGlove
interface DBGlove {
id: number
steamid: string
loadoutid: number
team: number
weapon_defindex: number
weapon_name: string
active: boolean | number
paintindex: number
paintseed: number
paintwear: number
created_at: string
updated_at: string
}API Types
Location: /types/api/
API Response Wrapper
interface APIResponse<T = unknown> {
success: boolean
data?: T
error?: APIError
meta?: APIMetadata
}
interface APIError {
code: string
message: string
details?: Record<string, unknown>
}
interface APIMetadata {
loadoutId?: number
steamId?: string
rows?: number
page?: number
totalPages?: number
}Usage:
type LoadoutsResponse = APIResponse<{ loadouts: DBLoadout[] }>
const response: LoadoutsResponse = await fetch('/api/loadouts')
if (response.success) {
console.log(response.data.loadouts)
}API Item Types
interface APIWeaponSkin {
id: number
defindex: number
paintindex: number
name: string
weapon: string
pattern: string
rarity: {
id: string
name: string
color: string
}
collection: string
min_float: number
max_float: number
image: string
statTrakAvailable: boolean
category: string
}
interface APISkin extends APIWeaponSkin {
// Alias for backward compatibility
}Admin API Types
Location: /types/api/admin.ts
/** Dashboard overview statistics */
interface AdminOverviewStats {
totalUsers: number
activeUsers7d: number
activeUsers30d: number
totalLoadouts: number
totalItems: { weapons: number; knives: number; gloves: number; agents: number; musicKits: number; pins: number }
bannedUsers: number
}
/** User details for admin view */
interface AdminUserDetails {
steamId: SteamId
loadoutCount: number
itemCounts: { weapons: number; knives: number; gloves: number; agents: number; musicKits: number; pins: number }
firstActivity: ISOTimestamp
lastActivity: ISOTimestamp
isBanned: boolean
banInfo?: { reason: string | null; bannedAt: ISOTimestamp; bannedBy: SteamId; expiresAt: ISOTimestamp | null }
}
/** User list item (summary for table display) */
interface AdminUserSummary {
steamId: SteamId
loadoutCount: number
totalItems: number
lastActivity: ISOTimestamp | null
isBanned: boolean
}
/** Activity data point for time-series charts */
interface AdminActivityData {
date: string
newUsers: number
activeUsers: number
loadoutsCreated: number
itemsSaved: number
}
/** Heatmap data for calendar visualization */
interface AdminHeatmapData { date: string; value: number }
/** Top user for leaderboard display */
interface AdminTopUser { steamId: SteamId; loadoutCount: number; totalItems: number }
/** Application setting */
interface AdminSetting {
key: string; value: string; type: 'string' | 'boolean' | 'number' | 'json'
description: string | null; updatedAt: ISOTimestamp; updatedBy: SteamId | null
}
/** Admin user info */
interface AdminInfo {
id: number; steamId: SteamId; role: 'admin' | 'superadmin'
permissions: string[]; createdBy: SteamId | null; createdAt: ISOTimestamp
}
/** Admin activity log entry */
interface AdminActivityLogEntry {
id: number; adminSteamId: SteamId; action: string
targetSteamId: SteamId | null; details: Record<string, unknown> | null; createdAt: ISOTimestamp
}
/** Request types */
interface AdminBanUserRequest { reason: string; duration?: number }
interface AdminUpdateSettingRequest { key: string; value: string | number | boolean }
interface AdminAddAdminRequest { steamId: string; role: 'admin' | 'superadmin' }
/** Query parameter types */
interface AdminUserSearchParams { search?: string; page?: number; limit?: number; bannedOnly?: boolean }
interface AdminActivityParams { range: '7d' | '30d' | '90d' }Server-Side Types
Location: /server/types/
Enhanced Item Types
interface IEnhancedItem {
weapon_defindex: number
defaultName: string
paintIndex: number
defaultImage: string
weapon_name: string
category: string
availableTeams: string
name: string
image: string
minFloat: number
maxFloat: number
rarity: RarityInfo
team: number | null
}
interface IEnhancedWeapon extends IEnhancedItem {
databaseInfo?: DatabaseInfo
type: 'weapon'
}
interface IEnhancedKnife extends IEnhancedItem {
databaseInfo?: DatabaseInfo
type: 'knife'
}
interface DatabaseInfo {
id: number
defindex: number
team: number
paintindex: number
paintseed: number
paintwear: number
stattrak_enabled: boolean
stattrak_count: number
nametag: string | null
active: boolean
}
interface RarityInfo {
id: string
name: string
color: string
}Inspect Types
interface InspectResult {
success: boolean
urlType: 'masked' | 'unmasked'
requiresSteam: boolean
item: InspectItem
parsed?: ParsedItemInfo
}
interface InspectItem {
defindex: number
paintindex: number
paintseed: number
paintwear: number
rarity?: number
quality?: number
statTrak?: {
enabled: boolean
count: number
}
nameTag?: string
stickers?: StickerData[]
keychain?: KeychainData
}
interface ParsedItemInfo {
weaponName: string
skinName: string
wear: string
floatValue: number
}Utility Types
Location: /types/core/common.ts
Loading States
enum LoadingState {
Idle = 'idle',
Loading = 'loading',
Success = 'success',
Error = 'error'
}Async Result
interface AsyncResult<T = unknown> {
state: LoadingState
data: T | null
error?: {
code: string
message: string
}
isLoading: boolean
isSuccess: boolean
isError: boolean
}Usage:
const asyncState = computed<AsyncResult<IEnhancedItem>>(() => ({
state: isLoading.value ? LoadingState.Loading :
error.value ? LoadingState.Error :
item.value ? LoadingState.Success : LoadingState.Idle,
data: item.value,
error: error.value ? { code: 'ERROR', message: error.value } : undefined,
isLoading: isLoading.value,
isSuccess: !!item.value && !error.value,
isError: !!error.value
}))User Profile
interface UserProfile {
steamId: string
username: string
avatar: string
profileUrl?: string
createdAt?: string
}Component Types
Location: /types/components/
Modal Types
interface ItemModalProps {
visible: boolean
selectedItem: IEnhancedItem | null
team: number
}
interface ItemModalEmits {
(e: 'update:visible', value: boolean): void
(e: 'save', config: ItemConfiguration): void
(e: 'close'): void
}Canvas Types
Location: /types/canvas.ts
interface CanvasSticker {
id: number
x: number
y: number
rotation: number
scale: number
wear: number
image: HTMLImageElement | null
}
interface CanvasConfig {
width: number
height: number
backgroundColor: string
gridSize: number
snapToGrid: boolean
}Item Type Enums
type ItemType = 'weapon' | 'knife' | 'glove' | 'agent' | 'musickit' | 'pin'
type WeaponCategory = 'rifles' | 'pistols' | 'smgs' | 'heavys'
type Team = 1 | 2 | 3 // 1=T, 2=CT, 3=BothType Conversion Best Practices
1. Always Use Branded Type Converters
// ✅ Good
const loadoutId = toLoadoutId(params.id)
// ❌ Bad
const loadoutId = Number(params.id) as LoadoutId2. Use Type Guards for Runtime Checks
// ✅ Good
if (isWeaponConfiguration(config)) {
console.log(config.stickers)
}
// ❌ Bad
if ((config as WeaponConfiguration).stickers) {
console.log((config as WeaponConfiguration).stickers)
}3. Leverage TypeScript Inference
// ✅ Good - TypeScript infers types
const weapon: IEnhancedWeapon = {
weapon_defindex: 7,
// ... TypeScript ensures all required properties
}
// ❌ Bad - Using 'any' defeats type safety
const weapon: any = { weapon_defindex: 7 }4. Use Generic Constraints
// ✅ Good
function processItem<T extends IEnhancedItem>(item: T): void {
console.log(item.weapon_defindex)
}
// ❌ Bad
function processItem(item: any): void {
console.log(item.weapon_defindex)
}Common Patterns
Pattern 1: API Response Handling
async function fetchLoadouts(steamId: SteamId): Promise<DBLoadout[]> {
const response = await fetch(`/api/loadouts?steamId=${steamId}`)
const data: APIResponse<{ loadouts: DBLoadout[] }> = await response.json()
if (!data.success) {
throw new Error(data.error?.message || 'Failed to fetch loadouts')
}
return data.data?.loadouts || []
}Pattern 2: Type-Safe State Updates
const state = ref<{
loadouts: DBLoadout[]
selectedId: LoadoutId | null
}>({
loadouts: [],
selectedId: null
})
// TypeScript ensures type safety
function selectLoadout(id: LoadoutId) {
state.value.selectedId = id // ✅ Type-safe
// state.value.selectedId = "123" // ❌ Error: string not assignable
}Pattern 3: Discriminated Unions
type ItemConfig =
| { type: 'weapon'; config: WeaponConfiguration }
| { type: 'knife'; config: KnifeConfiguration }
| { type: 'glove'; config: GloveConfiguration }
function processConfig(item: ItemConfig) {
switch (item.type) {
case 'weapon':
// TypeScript knows item.config is WeaponConfiguration
console.log(item.config.stickers)
break
case 'knife':
// TypeScript knows item.config is KnifeConfiguration
console.log(item.config.statTrak)
break
case 'glove':
// TypeScript knows item.config is GloveConfiguration
console.log(item.config.wear)
break
}
}Migration Guide
From Untyped to Typed
Before:
function getLoadout(id: any) {
return fetch(`/api/loadouts/${id}`)
}
const loadoutId = Number(params.id)
getLoadout(loadoutId)After:
function getLoadout(id: LoadoutId): Promise<DBLoadout> {
return fetch(`/api/loadouts/${id}`).then(r => r.json())
}
const loadoutId = toLoadoutId(params.id)
getLoadout(loadoutId)From Loose Types to Branded Types
Before:
interface Weapon {
id: number
steamId: string
defindex: number
paintIndex: number
}After:
interface Weapon {
id: number
steamId: SteamId
defindex: Defindex
paintIndex: PaintIndex
}Type Safety Benefits
1. Compile-Time Error Detection
const loadoutId = toLoadoutId(1)
const steamId = toSteamId("76561198012345678")
// ❌ Compile error: LoadoutId not assignable to SteamId
loadoutStore.fetchLoadouts(loadoutId)
// ✅ Correct
loadoutStore.fetchLoadouts(steamId)2. Autocomplete & IntelliSense
TypeScript provides autocomplete for all type properties:
const weapon: IEnhancedWeapon = {
// IDE suggests all required properties
weapon_defindex: 7,
defaultName: "AK-47",
// ...
}3. Refactoring Safety
When types change, TypeScript flags all affected code:
// Change LoadoutId from number to string
type LoadoutId = string & { readonly __brand: 'LoadoutId' }
// TypeScript will flag all places that assumed LoadoutId was a numberRelated Documentation
- Composables Reference - Composable APIs using these types
- Stores Reference - Pinia stores using these types
- API Reference - API endpoints and their types
- Components - Component prop types
TypeScript Configuration
The project uses strict TypeScript configuration:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}Contributing
When adding new types:
Choose Appropriate Location:
- Core types →
/types/core/ - API types →
/types/api/ - Database types →
/types/database/ - Business logic →
/types/business/
- Core types →
Add JSDoc Comments:
typescript/** * Description of the type * * @example * ```typescript * const example: MyType = { ... } * ``` */ export interface MyType { // ... }Export from index.ts:
typescriptexport type { MyType } from './path/to/type'Update This Documentation
Write Type Tests (if applicable):
typescriptimport { expectType } from 'tsd' expectType<LoadoutId>(toLoadoutId(1))