# How to get type-safe code from Cursor

- Tool: Cursor
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Cursor Free+, TypeScript projects
- Last updated: March 2026

## TL;DR

Cursor often generates JavaScript-style functions with implicit any types, missing return types, and untyped parameters. By adding strict typing rules to .cursorrules, enabling TypeScript strict mode in your tsconfig.json, and referencing your type definitions with @file, you ensure every function Cursor generates has explicit parameter types, return types, and no implicit any.

## Getting type-safe code from Cursor

Even in TypeScript projects, Cursor sometimes generates functions without explicit types, relying on inference or using any. This produces fragile code that loses the benefits of TypeScript. This tutorial shows how to configure Cursor to always generate explicitly typed, strict-mode-compatible code.

## Before you start

- Cursor installed with a TypeScript project
- tsconfig.json with strict mode enabled
- Type definitions for your domain objects
- Familiarity with Cursor Chat (Cmd+L) and Cmd+K

## Step-by-step guide

### 1. Enable strict TypeScript in tsconfig.json

Ensure your tsconfig.json has strict mode enabled. This catches Cursor-generated code that lacks types at compile time, providing an extra safety net.

```
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "forceConsistentCasingInImports": true
  }
}
```

**Expected result:** TypeScript compiler rejects any function without explicit types.

### 2. Add strict typing rules to .cursor/rules

Create rules that mandate explicit types on every function Cursor generates. This applies to Chat, Composer, and inline edits.

```
---
description: TypeScript strict typing rules
globs: "src/**/*.ts,src/**/*.tsx"
alwaysApply: true
---

## TypeScript Typing Rules
- ALWAYS add explicit return types to all functions
- ALWAYS type all function parameters (no implicit any)
- NEVER use 'any' type — use 'unknown' if type is uncertain
- Use interface for object shapes, type for unions/intersections
- Use readonly for arrays and objects that should not be mutated
- Prefer Record<string, T> over { [key: string]: T }
- Use branded types for IDs: type UserId = string & { __brand: 'UserId' }
- Import types with 'import type' syntax for tree-shaking
```

**Expected result:** Cursor generates fully typed code with explicit return types and no implicit any.

### 3. Generate a typed function with Cursor

Test your setup by asking Cursor to generate a function. Reference your type definitions so Cursor uses them correctly.

```
// Cursor Chat prompt (Cmd+L):
// @src/types/user.ts Create a function called formatUserDisplay
// that takes a User object and returns a formatted display
// string. Include explicit parameter and return types.

import type { User } from '@/types/user';

export function formatUserDisplay(user: User): string {
  const name = user.displayName || `${user.firstName} ${user.lastName}`;
  const role = user.role.charAt(0).toUpperCase() + user.role.slice(1);
  return `${name} (${role})`;
}
```

> Pro tip: Use 'import type' instead of 'import' for type-only imports. This ensures types are removed during compilation and do not affect bundle size.

**Expected result:** A function with explicit parameter type (User) and return type (string).

### 4. Audit existing code for missing types

Use Cursor to find functions that lack explicit types across your codebase. This catches code generated before you set up the rules.

```
// Cursor Chat prompt (Cmd+L, Ask mode):
// @codebase Find all exported functions in src/ that:
// 1. Have parameters without explicit types
// 2. Are missing explicit return type annotations
// 3. Use 'any' type anywhere
// List each file, function name, and what needs to be fixed.
```

**Expected result:** A list of all untyped or loosely typed functions in your codebase.

### 5. Fix untyped functions with Cmd+K

For each untyped function found, select it and use Cmd+K to add explicit types. Reference the relevant type files so Cursor uses your domain types correctly.

```
// Select the function, press Cmd+K:
// @src/types/user.ts @src/types/order.ts
// Add explicit TypeScript types to all parameters and
// the return type. Use our domain types (User, Order).
// Replace any 'any' with the correct specific type.
// Use 'unknown' only if the type genuinely cannot be known.
```

**Expected result:** All parameters and return values have explicit, correct TypeScript types.

## Complete code example

File: `.cursor/rules/typescript.mdc`

```markdown
---
description: Strict TypeScript typing conventions
globs: "src/**/*.{ts,tsx}"
alwaysApply: true
---

## Type Safety Rules
- EVERY function must have explicit return type annotation
- EVERY parameter must have an explicit type
- NEVER use 'any' — use 'unknown' + type narrowing if needed
- Use 'import type { X }' for type-only imports
- Use 'readonly' for parameters that should not be mutated
- Use 'as const' for literal objects and arrays

## Preferred Patterns
- interface for object shapes (extendable)
- type for unions, intersections, and mapped types
- Record<K, V> over { [key: K]: V }
- NonNullable<T> over T & {}
- Branded types for domain IDs:
  type UserId = string & { readonly __brand: unique symbol }

## Function Signatures
- export function name(param: Type): ReturnType { }
- Arrow functions: const name = (param: Type): ReturnType => { }
- Async functions: async function name(): Promise<ReturnType> { }
- Callbacks: callback: (error: Error | null, result: T) => void

## Generics
- Use descriptive generic names: <TItem>, <TResponse>, not <T>
- Constrain generics: <TItem extends BaseItem>
- Default generics where sensible: <TError = Error>
```

## Common mistakes

- **Cursor using 'any' for unknown types** — When Cursor does not know the exact type, it defaults to 'any' instead of 'unknown', disabling type checking entirely. Fix: Add 'NEVER use any, use unknown with type narrowing' to .cursorrules.
- **Missing return type on async functions** — Cursor generates async functions returning Promise<any> instead of Promise<User> because it does not infer the return type from the body. Fix: Always specify the return type in your prompt: 'returns Promise<User>' and add the rule to mandate explicit return types.
- **Using 'import { User }' instead of 'import type { User }'** — Regular imports of types can cause issues with module bundlers and tree-shaking. Cursor uses regular imports by default. Fix: Add 'Use import type for type-only imports' to .cursorrules.

## Best practices

- Enable strict: true in tsconfig.json as a safety net for Cursor output
- Add .cursorrules mandating explicit types on all functions
- Use import type syntax for type-only imports
- Reference domain type files with @file when generating typed code
- Prefer unknown over any for uncertain types with type narrowing
- Use branded types for domain IDs to prevent mixing different ID types
- Audit your codebase periodically with @codebase for missing type annotations

## Frequently asked questions

### Why does Cursor generate untyped code in a TypeScript project?

Cursor's models were trained on both JavaScript and TypeScript. Without explicit rules, it sometimes generates JS-style code. Adding .cursorrules for strict typing solves this.

### Is 'any' ever acceptable?

Rarely. Use any only for third-party library interop where types are genuinely unavailable. Even then, wrap it in a typed function boundary. Never use any in your own domain code.

### Should I use interfaces or type aliases?

Use interfaces for object shapes that may be extended (User, Order). Use type for unions (Status = 'active' | 'inactive'), intersections, and mapped types. Add this preference to .cursorrules.

### Can Cursor infer types instead of explicit annotations?

TypeScript can infer types, but explicit annotations serve as documentation and catch regressions. For exported functions, always add explicit types. For local variables, inference is fine.

### How do I handle types from external libraries?

Reference the library's type definitions with @node_modules/@types/library or @file in your prompt. If the library lacks types, ask Cursor to create a declaration file (.d.ts).

---

Source: https://www.rapidevelopers.com/cursor-tutorial/how-to-force-cursor-ai-to-reference-typescript-types-when-generating-function-signatures
© RapidDev — https://www.rapidevelopers.com/cursor-tutorial/how-to-force-cursor-ai-to-reference-typescript-types-when-generating-function-signatures
