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

utilities-custom-mapped-types.mdreferences/

Custom Mapped Types

Creating reusable type transformations with mapped types.

Basic Mapped Type Pattern

type Mapped<T> = {
  [K in keyof T]: T[K]
};

// With transformation
type Nullable<T> = {
  [K in keyof T]: T[K] | null
};

type Optional<T> = {
  [K in keyof T]?: T[K]
};

Modifier Flags

Adding/Removing readonly

// Add readonly
type Immutable<T> = {
  +readonly [K in keyof T]: T[K]
};

// Remove readonly
type Mutable<T> = {
  -readonly [K in keyof T]: T[K]
};

Adding/Removing optional

// Make all optional
type AllOptional<T> = {
  [K in keyof T]?: T[K]
};

// Make all required
type AllRequired<T> = {
  [K in keyof T]-?: T[K]
};

Advanced Mapped Types

Getters

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};

type User = { name: string; age: number };
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number }

Setters

type Setters<T> = {
  [K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void
};

type UserSetters = Setters<User>;
// { setName: (value: string) => void; setAge: (value: number) => void }

Combined Accessors

type Accessors<T> = Getters<T> & Setters<T>;

class UserClass implements Accessors<User> {
  private data: User = { name: '', age: 0 };
  
  getName() { return this.data.name; }
  setName(value: string) { this.data.name = value; }
  getAge() { return this.data.age; }
  setAge(value: number) { this.data.age = value; }
}

Promisify

Convert sync functions to async.

type Promisify<T> = {
  [K in keyof T]: T[K] extends (...args: infer A) => infer R
    ? (...args: A) => Promise<R>
    : T[K]
};

interface SyncAPI {
  getUser: (id: string) => User;
  createUser: (data: UserInput) => User;
}

type AsyncAPI = Promisify<SyncAPI>;
// {
//   getUser: (id: string) => Promise<User>;
//   createUser: (data: UserInput) => Promise<User>;
// }

DeepReadonly & DeepPartial

Recursive type transformations.

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K]
};

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object
    ? DeepPartial<T[K]>
    : T[K]
};

type Config = {
  database: {
    host: string;
    port: number;
    credentials: {
      username: string;
      password: string;
    };
  };
};

type ImmutableConfig = DeepReadonly<Config>;
// All properties readonly, including nested

Filtering Properties

By Type

type PickByType<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K]
};

type OmitByType<T, U> = {
  [K in keyof T as T[K] extends U ? never : K]: T[K]
};

interface Mixed {
  id: string;
  name: string;
  age: number;
  active: boolean;
}

type StringProps = PickByType<Mixed, string>;
// { id: string; name: string }

type NotStrings = OmitByType<Mixed, string>;
// { age: number; active: boolean }

By Key Pattern

type PickByKeyPrefix<T, Prefix extends string> = {
  [K in keyof T as K extends `${Prefix}${string}` ? K : never]: T[K]
};

interface User {
  id: string;
  name: string;
  isActive: boolean;
  isAdmin: boolean;
  createdAt: Date;
}

type BooleanFlags = PickByKeyPrefix<User, 'is'>;
// { isActive: boolean; isAdmin: boolean }

Practical Patterns

API Response Wrapper

type APIResponse<T> = {
  [K in keyof T]: {
    data: T[K];
    loading: boolean;
    error: Error | null;
  }
};

interface Data {
  user: User;
  posts: Post[];
}

type WrappedData = APIResponse<Data>;
// {
//   user: { data: User; loading: boolean; error: Error | null };
//   posts: { data: Post[]; loading: boolean; error: Error | null };
// }

Event Handlers

type EventHandlers<T> = {
  [K in keyof T as `on${Capitalize<string & K>}Change`]: (
    value: T[K]
  ) => void
};

interface FormData {
  username: string;
  email: string;
  age: number;
}

type FormHandlers = EventHandlers<FormData>;
// {
//   onUsernameChange: (value: string) => void;
//   onEmailChange: (value: string) => void;
//   onAgeChange: (value: number) => void;
// }

Validation Schema

type ValidationSchema<T> = {
  [K in keyof T]: {
    required: boolean;
    validator: (value: T[K]) => boolean;
    message: string;
  }
};

const userSchema: ValidationSchema<User> = {
  name: {
    required: true,
    validator: (v) => v.length > 0,
    message: 'Name is required'
  },
  age: {
    required: true,
    validator: (v) => v > 0,
    message: 'Age must be positive'
  }
};

Boxed Types

Wrap primitives in objects.

type Boxed<T> = {
  [K in keyof T]: { value: T[K] }
};

type Unbox<T> = {
  [K in keyof T]: T[K] extends { value: infer V } ? V : T[K]
};

interface Config {
  port: number;
  host: string;
}

type BoxedConfig = Boxed<Config>;
// { port: { value: number }; host: { value: string } }

type UnboxedConfig = Unbox<BoxedConfig>;
// { port: number; host: string }

Best Practices

  1. Name mapped types clearly: Use descriptive names like Promisify, Getters
  2. Document transformations: Explain what the mapped type does
  3. Limit recursion depth: Deep recursion can impact performance
  4. Use with const assertions: Preserve literal types
  5. Test with complex types: Ensure mapped types work with edge cases

Common Patterns

State Management

type StateSlice<T> = {
  [K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void
} & {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
} & {
  [K in keyof T as `reset${Capitalize<string & K>}`]: () => void
};

Dependency Injection

type Injectable<T> = {
  [K in keyof T]: T[K] extends (...args: infer A) => infer R
    ? (...args: [...A, dependencies: Record<string, any>]) => R
    : T[K]
};

Anti-Patterns

Overly complex mapped types

type Bad<T> = {
  [K in keyof T as K extends string
    ? T[K] extends number
      ? `num_${K}`
      : T[K] extends string
      ? `str_${K}`
      : K
    : K]: T[K]
};

Split into smaller utilities

type PrefixNumbers<T> = { [K in keyof T as /* ... */]: T[K] };
type PrefixStrings<T> = { [K in keyof T as /* ... */]: T[K] };
type Good<T> = PrefixNumbers<T> & PrefixStrings<T>;

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