Skip to content

TypeScript Style Guide

Google TypeScript Style standards for Fulcrum dashboard and SDKs.


Overview

This guide summarizes key rules from the Google TypeScript Style Guide, enforced by gts. It applies to:

  • Dashboard (Next.js)
  • TypeScript SDK
  • Build tooling

Language Features

Variable Declarations

// Use const by default
const config = loadConfig();

// Use let only when reassignment is needed
let count = 0;
count++;

// NEVER use var
var x = 5;  // FORBIDDEN

Modules

// Use ES6 modules
import { PolicyEngine } from './policy-engine';
export { evaluatePolicy, PolicyResult };

// NEVER use namespace
namespace MyApp {  // FORBIDDEN
  // ...
}

Exports

// Use named exports
export { PolicyEngine };
export { helper1, helper2 };

// DO NOT use default exports
export default PolicyEngine;  // BAD

Classes

Visibility Modifiers

class PolicyService {
  // NEVER use public (it's the default)
  public name: string;  // BAD
  name: string;         // GOOD

  // Use private/protected to restrict access
  private cache: Map<string, Policy>;
  protected logger: Logger;

  // Mark readonly for properties set only in constructor
  readonly id: string;

  constructor(id: string) {
    this.id = id;
  }
}

Private Fields

// DO NOT use #private fields
class Service {
  #secret: string;  // BAD - ECMAScript private field
}

// Use TypeScript's private modifier
class Service {
  private secret: string;  // GOOD
}

Functions

Function Declarations vs Expressions

// Prefer declarations for named functions
function evaluatePolicy(policyId: string): PolicyResult {
  // ...
}

// Use arrow functions for anonymous/callbacks
const policies = items.filter((item) => item.type === 'policy');

const handler = async (req: Request) => {
  // ...
};

Strings

// Use single quotes
const name = 'fulcrum';

// Use template literals for interpolation
const message = `Hello, ${name}!`;

// Use template literals for multi-line
const query = `
  SELECT *
  FROM policies
  WHERE tenant_id = $1
`;

Type System

Type Assertions

// AVOID type assertions
const x = value as SomeType;  // Avoid unless necessary
const y = value!;             // Avoid non-null assertions

// If you must use them, justify with a comment
// Type assertion is safe here because we validated above
const config = data as Config;

Avoid any

// AVOID any
function process(data: any) { }  // BAD

// Prefer unknown
function process(data: unknown) {
  if (isPolicy(data)) {
    // Now TypeScript knows it's a Policy
  }
}

// Or use a specific type
function process(data: Policy | PolicyGroup) { }

Optional vs undefined

// Prefer optional parameters
function greet(name?: string) { }

// Over union with undefined
function greet(name: string | undefined) { }  // Less preferred

Array Types

// Use T[] for simple types
const policies: Policy[] = [];
const names: string[] = [];

// Use Array<T> for complex unions
const items: Array<string | number> = [];
const results: Array<{ id: string; value: number }> = [];

Avoid {}

// DO NOT use {}
const data: {} = getValue();  // BAD

// Use appropriate alternatives
const data: unknown = getValue();
const data: Record<string, unknown> = getValue();
const data: object = getValue();

Equality

// ALWAYS use triple equals
if (a === b) { }
if (a !== b) { }

// NEVER use loose equality
if (a == b) { }   // BAD
if (a != b) { }   // BAD

Disallowed Features

Feature Alternative
any type unknown or specific types
var const or let
namespace ES6 modules
default exports named exports
#private fields private modifier
const enum plain enum
eval() (none - forbidden)
Wrapper objects primitives
ASI reliance explicit semicolons

Naming Conventions

Type Style Example
Classes UpperCamelCase PolicyEngine
Interfaces UpperCamelCase PolicyResult
Types UpperCamelCase EvaluationStatus
Enums UpperCamelCase Decision
Enum values CONSTANT_CASE ALLOW, DENY
Functions lowerCamelCase evaluatePolicy
Methods lowerCamelCase getConfig
Variables lowerCamelCase policyResult
Constants CONSTANT_CASE MAX_RETRIES

No Underscores

// DO NOT use _ prefix/suffix
private _name: string;   // BAD
const name_: string;     // BAD

// Use clear names
private name: string;    // GOOD
const userName: string;  // GOOD

Comments and Documentation

JSDoc

/**
 * Evaluates a policy against the given request.
 *
 * @param policyId - The policy identifier
 * @param request - The request to evaluate
 * @returns The evaluation result
 * @throws {PolicyNotFoundError} When policy doesn't exist
 */
function evaluatePolicy(policyId: string, request: Request): PolicyResult {
  // ...
}

Type Redundancy

// DO NOT declare types in JSDoc (TypeScript has them)
/**
 * @param {string} name - The name  // BAD - redundant
 */
function greet(name: string) { }

// Just describe the parameter
/**
 * @param name - The user's display name
 */
function greet(name: string) { }

Comments Must Add Value

// BAD - restates the code
// Increment counter
counter++;

// GOOD - explains why
// Rate limit requires tracking requests per second
counter++;

React/Next.js Patterns (Dashboard)

Component Structure

// Named export for components
export function PolicyList({ policies }: PolicyListProps) {
  return (
    <ul>
      {policies.map((policy) => (
        <PolicyItem key={policy.id} policy={policy} />
      ))}
    </ul>
  );
}

// Interface for props
interface PolicyListProps {
  policies: Policy[];
  onSelect?: (policy: Policy) => void;
}

Hooks

// Custom hooks start with "use"
function usePolicies(tenantId: string) {
  const [policies, setPolicies] = useState<Policy[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchPolicies(tenantId).then(setPolicies);
    setLoading(false);
  }, [tenantId]);

  return { policies, loading };
}

See Also


Document Version: 1.0 Last Updated: January 20, 2026