---
title: "TypeScript"
description: "TypeScript integration and best practices for the TraceRTM JavaScript SDK"
---

# TypeScript

The TraceRTM SDK is written in TypeScript and provides complete type definitions.

## Setup

### Installation

The SDK includes TypeScript definitions by default:

```bash
npm install @tracertm/sdk
```

### TypeScript Configuration

Recommended `tsconfig.json` settings:

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true
  }
}
```

---

## Type Imports

### Import Types

```typescript
import {
  TraceRTMClient,
  // Core types
  Project,
  Item,
  Link,
  User,
  // Input types
  CreateProjectInput,
  UpdateProjectInput,
  CreateItemInput,
  UpdateItemInput,
  CreateLinkInput,
  // Response types
  PaginatedResponse,
  SearchResults,
  CoverageReport,
  TraceabilityMatrix,
  ImpactAnalysis,
  // Enums/Literals
  ItemType,
  LinkType,
  Priority,
  StatusCategory,
  // Errors
  TraceRTMError,
  NotFoundError,
  ValidationError,
} from "@tracertm/sdk";
```

### Type-Only Imports

For better tree-shaking:

```typescript
import type {
  Project,
  Item,
  Link,
  ItemType,
  Priority,
} from "@tracertm/sdk";

import { TraceRTMClient } from "@tracertm/sdk";
```

---

## Core Types

### Project Type

```typescript
interface Project {
  id: string;
  name: string;
  key: string;
  description?: string;
  ownerId: string;
  settings: ProjectSettings;
  createdAt: string;
  updatedAt: string;
}

interface ProjectSettings {
  defaultItemType?: ItemType;
  allowedItemTypes?: ItemType[];
  requireApproval?: boolean;
  customStatuses?: StatusConfig[];
  customFields?: FieldConfig[];
}
```

### Item Type

```typescript
interface Item {
  id: string;
  projectId: string;
  type: ItemType;
  title: string;
  description?: string;
  status: string;
  statusCategory: StatusCategory;
  priority?: Priority;
  tags: string[];
  assigneeId?: string;
  createdBy: string;
  dueDate?: string;
  fields: Record<string, unknown>;
  parentId?: string;
  links?: Link[];
  createdAt: string;
  updatedAt: string;
}
```

### Link Type

```typescript
interface Link {
  id: string;
  sourceId: string;
  targetId: string;
  type: LinkType;
  description?: string;
  createdBy: string;
  createdAt: string;
}
```

---

## Literal Types

### ItemType

```typescript
type ItemType =
  | "requirement"
  | "test_case"
  | "user_story"
  | "bug"
  | "task"
  | "risk"
  | "decision"
  | "epic"
  | "feature";

// Usage
const type: ItemType = "requirement"; // OK
const invalid: ItemType = "invalid"; // Error!
```

### LinkType

```typescript
type LinkType =
  | "traces_to"
  | "verified_by"
  | "implements"
  | "parent_of"
  | "relates_to"
  | "depends_on"
  | "duplicates"
  | "blocks"
  | "derived_from"
  | "satisfies";
```

### Priority

```typescript
type Priority = "critical" | "high" | "medium" | "low";
```

### StatusCategory

```typescript
type StatusCategory = "initial" | "in_progress" | "done";
```

---

## Generic Types

### PaginatedResponse

```typescript
interface PaginatedResponse<T> {
  data: T[];
  total: number;
  limit: number;
  offset: number;
  hasMore: boolean;
}

// Usage
const items: PaginatedResponse<Item> = await client.items.list({
  projectId: "proj_123",
});

items.data.forEach((item: Item) => {
  console.log(item.title);
});
```

### Working with Generics

```typescript
// Generic helper function
async function getAllPages<T>(
  fetcher: (offset: number) => Promise<PaginatedResponse<T>>
): Promise<T[]> {
  const results: T[] = [];
  let offset = 0;

  while (true) {
    const response = await fetcher(offset);
    results.push(...response.data);

    if (!response.hasMore) break;
    offset += response.data.length;
  }

  return results;
}

// Usage
const allItems = await getAllPages<Item>((offset) =>
  client.items.list({ projectId: "proj_123", offset })
);
```

---

## Type Guards

### Built-in Type Guards

```typescript
import {
  isProject,
  isItem,
  isLink,
  isItemType,
  isLinkType,
  isPriority,
} from "@tracertm/sdk";

// Usage
function processData(data: unknown) {
  if (isItem(data)) {
    // TypeScript knows data is Item
    console.log(data.title);
    console.log(data.projectId);
  }
}

function validateType(type: string): ItemType | null {
  if (isItemType(type)) {
    return type; // TypeScript knows type is ItemType
  }
  return null;
}
```

### Custom Type Guards

```typescript
// Check if item is a requirement
function isRequirement(item: Item): item is Item & { type: "requirement" } {
  return item.type === "requirement";
}

// Check if item has links
function hasLinks(item: Item): item is Item & { links: Link[] } {
  return Array.isArray(item.links) && item.links.length > 0;
}

// Usage
const items = await client.items.list({ projectId: "proj_123" });

const requirements = items.data.filter(isRequirement);
// requirements is (Item & { type: "requirement" })[]

const linkedItems = items.data.filter(hasLinks);
// linkedItems is (Item & { links: Link[] })[]
```

---

## Discriminated Unions

### Error Handling

```typescript
import { TraceRTMError, NotFoundError, ValidationError } from "@tracertm/sdk";

function handleError(error: TraceRTMError): string {
  switch (error.code) {
    case "NOT_FOUND":
      return "Resource not found";
    case "VALIDATION_ERROR":
      // TypeScript knows error is ValidationError
      return `Validation failed: ${(error as ValidationError).details
        .map((d) => d.message)
        .join(", ")}`;
    case "RATE_LIMITED":
      return "Please slow down";
    default:
      return "An error occurred";
  }
}
```

### Response Types

```typescript
type ApiResponse<T> =
  | { success: true; data: T }
  | { success: false; error: TraceRTMError };

async function fetchItem(id: string): Promise<ApiResponse<Item>> {
  try {
    const data = await client.items.get(id);
    return { success: true, data };
  } catch (error) {
    if (error instanceof TraceRTMError) {
      return { success: false, error };
    }
    throw error;
  }
}

// Usage
const result = await fetchItem("item_123");

if (result.success) {
  // TypeScript knows result.data is Item
  console.log(result.data.title);
} else {
  // TypeScript knows result.error is TraceRTMError
  console.log(result.error.message);
}
```

---

## Utility Types

### Partial Updates

```typescript
// Update only specific fields
async function updateItemFields(
  id: string,
  fields: Partial<Pick<Item, "title" | "description" | "priority">>
) {
  return client.items.update(id, fields);
}

// Usage
await updateItemFields("item_123", { priority: "high" });
```

### Required Fields

```typescript
// Ensure all required fields are provided
type RequiredItemInput = Required<
  Pick<CreateItemInput, "projectId" | "type" | "title">
>;

function createItem(input: RequiredItemInput & Partial<CreateItemInput>) {
  return client.items.create(input);
}
```

### Readonly Types

```typescript
// Immutable item reference
type ReadonlyItem = Readonly<Item>;

function displayItem(item: ReadonlyItem) {
  console.log(item.title);
  // item.title = "New"; // Error! Cannot modify readonly property
}
```

---

## Extending Types

### Custom Item Types

```typescript
// Extend Item with custom fields
interface RequirementItem extends Item {
  type: "requirement";
  fields: {
    acceptanceCriteria?: string;
    source?: string;
    stakeholder?: string;
  };
}

// Type assertion helper
function asRequirement(item: Item): RequirementItem | null {
  if (item.type !== "requirement") return null;
  return item as RequirementItem;
}
```

### Branded Types

```typescript
// Create branded types for IDs
type ProjectId = string & { __brand: "ProjectId" };
type ItemId = string & { __brand: "ItemId" };

function createProjectId(id: string): ProjectId {
  return id as ProjectId;
}

function createItemId(id: string): ItemId {
  return id as ItemId;
}

// Prevents mixing up IDs
async function getItem(projectId: ProjectId, itemId: ItemId) {
  const item = await client.items.get(itemId);
  if (item.projectId !== projectId) {
    throw new Error("Item does not belong to project");
  }
  return item;
}

// Usage
const projId = createProjectId("proj_123");
const itemId = createItemId("item_456");

await getItem(projId, itemId); // OK
await getItem(itemId, projId); // Error! Types don't match
```

---

## Async Types

### Promise Types

```typescript
// Explicitly type async functions
async function getProject(id: string): Promise<Project> {
  return client.projects.get(id);
}

async function getProjectOrNull(id: string): Promise<Project | null> {
  try {
    return await client.projects.get(id);
  } catch {
    return null;
  }
}

// Inferred return type
const createProject = async (name: string, key: string) => {
  return client.projects.create({ name, key });
};
// Return type: Promise<Project>
```

### Async Iterables

```typescript
async function* iterateItems(
  projectId: string
): AsyncGenerator<Item, void, unknown> {
  let offset = 0;
  const limit = 100;

  while (true) {
    const response = await client.items.list({ projectId, limit, offset });

    for (const item of response.data) {
      yield item;
    }

    if (!response.hasMore) break;
    offset += limit;
  }
}

// Usage
for await (const item of iterateItems("proj_123")) {
  // item is typed as Item
  console.log(item.title);
}
```

---

## Strict Mode Benefits

### Null Checks

```typescript
// With strict null checks
async function getItemTitle(id: string): Promise<string> {
  const item = await client.items.get(id);

  // item.description is string | undefined
  if (item.description) {
    return `${item.title}: ${item.description}`;
  }

  return item.title;
}
```

### No Implicit Any

```typescript
// Without implicit any, you must type everything
const processItems = (items: Item[]) => {
  return items.map((item) => ({
    id: item.id,
    title: item.title,
  }));
};

// Return type inferred: { id: string; title: string; }[]
```

### Strict Property Initialization

```typescript
class ItemService {
  private client: TraceRTMClient;
  private projectId: string;

  constructor(apiKey: string, projectId: string) {
    this.client = new TraceRTMClient({ apiKey });
    this.projectId = projectId;
  }

  async list() {
    return this.client.items.list({ projectId: this.projectId });
  }
}
```

---

## Best Practices

### 1. Use Type Imports

```typescript
// Prefer type imports for types only
import type { Item, Project } from "@tracertm/sdk";
import { TraceRTMClient } from "@tracertm/sdk";
```

### 2. Avoid Type Assertions

```typescript
// Bad - unsafe assertion
const item = response as Item;

// Good - type guard
if (isItem(response)) {
  const item = response;
}
```

### 3. Use Const Assertions

```typescript
// Literal types
const itemTypes = ["requirement", "test_case"] as const;
type SelectedTypes = (typeof itemTypes)[number];
// SelectedTypes = "requirement" | "test_case"
```

### 4. Document with JSDoc

```typescript
/**
 * Creates a new requirement in the project
 * @param projectId - The project to add the requirement to
 * @param title - The requirement title
 * @param options - Additional options
 * @returns The created requirement item
 */
async function createRequirement(
  projectId: string,
  title: string,
  options?: {
    description?: string;
    priority?: Priority;
  }
): Promise<Item> {
  return client.items.create({
    projectId,
    type: "requirement",
    title,
    ...options,
  });
}
```

---

## Related Documentation

- [Installation](/docs/api-reference/sdks/javascript/installation) - Setup guide
- [Types](/docs/api-reference/sdks/javascript/types) - Type reference
- [API Reference](/docs/api-reference/sdks/javascript/api-reference) - Full API
