CtrlK
BlogDocsLog inGet started
Tessl Logo

pantheon-ai/typescript-advanced

Comprehensive TypeScript guidance covering compiler configuration, advanced types, utility types, type guards, strict mode workflows, and documentation patterns; use when configuring tsconfig, designing complex generics, making illegal states unrepresentable, fixing type errors, or writing testable and maintainable type-safe APIs.

Overall
score

99%

Does it follow best practices?

Validation for skill structure

Overview
Skills
Evals
Files

patterns-api-client.mdreferences/

Type-Safe API Client

Building API clients with route configuration and full type safety.

Basic API Client

type Routes = {
  '/users': {
    GET: { response: User[] };
    POST: { body: CreateUserDTO; response: User };
  };
  '/users/:id': {
    GET: { response: User };
    PUT: { body: UpdateUserDTO; response: User };
    DELETE: { response: void };
  };
  '/posts': {
    GET: { query: { page: number; limit: number }; response: Post[] };
  };
};

class ApiClient<T extends Record<string, Record<string, unknown>>> {
  constructor(private baseUrl: string) {}

  async request<
    Path extends keyof T,
    Method extends keyof T[Path]
  >(
    path: Path,
    method: Method,
    config?: T[Path][Method] extends { body: infer B } ? { body: B } : {}
      & T[Path][Method] extends { query: infer Q } ? { query: Q } : {}
  ): Promise<T[Path][Method] extends { response: infer R } ? R : never> {
    const url = new URL(path as string, this.baseUrl);
    
    if (config && 'query' in config) {
      Object.entries(config.query as Record<string, unknown>).forEach(([key, value]) => {
        url.searchParams.append(key, String(value));
      });
    }

    const response = await fetch(url.toString(), {
      method: method as string,
      headers: {
        'Content-Type': 'application/json',
      },
      body: config && 'body' in config 
        ? JSON.stringify(config.body) 
        : undefined,
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return response.json();
  }
}

// Usage with full type safety
const client = new ApiClient<Routes>('https://api.example.com');

// GET /users - no config needed
const users = await client.request('/users', 'GET');
// users is typed as User[]

// POST /users - requires body
const newUser = await client.request('/users', 'POST', {
  body: { name: 'John', email: 'john@example.com' }
});
// newUser is typed as User

// GET /posts - requires query params
const posts = await client.request('/posts', 'GET', {
  query: { page: 1, limit: 10 }
});
// posts is typed as Post[]

With Path Parameters

type PathParams<Path extends string> = 
  Path extends `${infer _Start}:${infer Param}/${infer Rest}`
    ? { [K in Param | keyof PathParams<`/${Rest}`>]: string }
    : Path extends `${infer _Start}:${infer Param}`
    ? { [K in Param]: string }
    : {};

class ApiClientWithParams<T extends Record<string, Record<string, unknown>>> {
  async request<
    Path extends keyof T & string,
    Method extends keyof T[Path]
  >(
    path: Path,
    method: Method,
    config: PathParams<Path> extends Record<string, never>
      ? T[Path][Method] extends { body: infer B } ? { body: B } : {}
      : { params: PathParams<Path> } 
        & (T[Path][Method] extends { body: infer B } ? { body: B } : {})
  ): Promise<T[Path][Method] extends { response: infer R } ? R : never> {
    let url = path as string;
    
    if ('params' in config) {
      Object.entries(config.params as Record<string, string>).forEach(([key, value]) => {
        url = url.replace(`:${key}`, value);
      });
    }

    // ... rest of implementation
  }
}

// Usage
const user = await client.request('/users/:id', 'GET', {
  params: { id: '123' }
});

With Response Validation

import { z } from 'zod';

type ValidatedRoutes = {
  '/users': {
    GET: { 
      response: User[];
      schema: z.ZodType<User[]>;
    };
  };
};

class ValidatedApiClient<T extends Record<string, Record<string, unknown>>> {
  async request<
    Path extends keyof T,
    Method extends keyof T[Path]
  >(
    path: Path,
    method: Method,
    schema: T[Path][Method] extends { schema: infer S } ? S : never
  ): Promise<T[Path][Method] extends { response: infer R } ? R : never> {
    const response = await fetch(/* ... */);
    const data = await response.json();
    
    // Runtime validation
    return (schema as z.ZodType).parse(data);
  }
}

Best Practices

  1. Define Route Types: Central route configuration with all endpoints
  2. Type Safety: Enforce correct method, body, and query params
  3. Path Parameters: Extract and validate path parameters from route strings
  4. Response Validation: Use Zod for runtime validation of responses
  5. Error Handling: Provide typed error responses

See Also

  • patterns-builder.md
  • guards-type-predicates.md
  • practices-runtime-validation.md

Install with Tessl CLI

npx tessl i pantheon-ai/typescript-advanced@0.1.1

references

compiler-module-resolution.md

compiler-performance.md

compiler-strict-mode.md

compiler-tsconfig.md

docs-adr-templates.md

docs-framework-docs.md

docs-jsdoc-patterns.md

docs-typedoc-config.md

guards-assertion-functions.md

guards-basic.md

guards-branded-types.md

guards-discriminated-unions.md

guards-exhaustiveness.md

guards-generic.md

guards-inference-infer.md

guards-inference-return.md

patterns-advanced-generics.md

patterns-api-client.md

patterns-branded-types.md

patterns-builder.md

patterns-deep-readonly.md

patterns-dependency-injection.md

patterns-event-emitter.md

patterns-form-validation.md

patterns-plugin-system.md

patterns-recursive-types.md

patterns-state-machine.md

patterns-type-safe-module.md

practices-illegal-states.md

practices-module-patterns.md

practices-runtime-validation.md

practices-type-first.md

types-conditional.md

types-generics.md

types-index-signatures.md

types-mapped.md

types-narrowing.md

types-template-literals.md

types-type-assertions.md

types-unions-intersections.md

utilities-custom-mapped-types.md

utilities-extract-exclude.md

utilities-key-remapping.md

utilities-nonnullable-awaited.md

utilities-partial-required.md

utilities-pick-omit.md

utilities-readonly-record.md

utilities-returntype-parameters.md

SKILL.md

tile.json