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

types-narrowing.mdreferences/

Type Narrowing and Guards

typeof Guards

Use typeof to narrow primitive types:

function process(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase(); // value is string here
  } else {
    return value.toFixed(2); // value is number here
  }
}

instanceof Guards

Narrow to class instances:

class Dog {
  bark() { console.log("Woof!"); }
}

class Cat {
  meow() { console.log("Meow!"); }
}

function speak(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark(); // animal is Dog here
  } else {
    animal.meow(); // animal is Cat here
  }
}

in Operator

Check for property existence:

interface User {
  name: string;
  email: string;
}

interface Guest {
  name: string;
  sessionId: string;
}

function greet(person: User | Guest) {
  if ("email" in person) {
    console.log(person.email); // person is User here
  } else {
    console.log(person.sessionId); // person is Guest here
  }
}

Custom Type Guards

Functions that return value is Type:

function isString(value: unknown): value is string {
  return typeof value === "string";
}

function isNumber(value: unknown): value is number {
  return typeof value === "number";
}

function process(value: unknown) {
  if (isString(value)) {
    return value.toUpperCase(); // value is string
  }
  if (isNumber(value)) {
    return value.toFixed(2); // value is number
  }
}

Generic Type Guards

function isArray<T>(value: unknown): value is T[] {
  return Array.isArray(value);
}

function isRecord<T extends object>(
  value: unknown
): value is Record<string, T> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

Discriminated Union Narrowing

type Success = { status: "success"; data: string };
type Failure = { status: "failure"; error: Error };
type Result = Success | Failure;

function handle(result: Result) {
  switch (result.status) {
    case "success":
      console.log(result.data); // result is Success
      break;
    case "failure":
      console.error(result.error); // result is Failure
      break;
  }
}

Assertion Functions

Functions that throw or narrow types:

function assert(condition: unknown): asserts condition {
  if (!condition) {
    throw new Error("Assertion failed");
  }
}

function assertIsString(value: unknown): asserts value is string {
  if (typeof value !== "string") {
    throw new Error("Not a string");
  }
}

function process(value: unknown) {
  assertIsString(value);
  // value is string from this point forward
  return value.toUpperCase();
}

Assertion with Parameter

function assertIsDefined<T>(
  value: T,
  message?: string
): asserts value is NonNullable<T> {
  if (value === null || value === undefined) {
    throw new Error(message ?? "Value is null or undefined");
  }
}

function process(value: string | null) {
  assertIsDefined(value);
  // value is string here
  return value.toUpperCase();
}

Equality Narrowing

function process(value: string | number, check: string | number) {
  if (value === check) {
    // Both value and check are narrowed to their intersection type
    console.log(value.toFixed(2)); // Only works if both can be number
  }
}

Truthiness Narrowing

function process(value: string | null | undefined) {
  if (value) {
    // value is string here (null and undefined are falsy)
    return value.toUpperCase();
  }
}

// Be careful with falsy values
function handleNumber(value: number | null) {
  if (value) {
    // This excludes 0, which might not be intended
    return value.toFixed(2);
  }
}

Null/Undefined Checks

function process(value: string | null | undefined) {
  if (value !== null && value !== undefined) {
    return value.toUpperCase(); // value is string
  }
  
  // Or use != (non-strict)
  if (value != null) {
    return value.toUpperCase(); // value is string (excludes null and undefined)
  }
}

Array.isArray Narrowing

function process(value: string | string[]) {
  if (Array.isArray(value)) {
    return value.join(", "); // value is string[]
  } else {
    return value.toUpperCase(); // value is string
  }
}

Advanced Patterns

hasProperty Type Guard Factory

function hasProperty<K extends string>(
  obj: unknown,
  key: K
): obj is Record<K, unknown> {
  return typeof obj === "object" && obj !== null && key in obj;
}

function process(value: unknown) {
  if (hasProperty(value, "name") && typeof value.name === "string") {
    console.log(value.name.toUpperCase());
  }
}

Type Predicate with Generic Constraint

interface Named {
  name: string;
}

function isNamed<T>(value: T): value is T & Named {
  return (
    typeof value === "object" &&
    value !== null &&
    "name" in value &&
    typeof (value as any).name === "string"
  );
}

Exhaustiveness Checking

type Shape = Circle | Square | Triangle;

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.size ** 2;
    case "triangle":
      return (shape.base * shape.height) / 2;
    default:
      // Ensures all cases are handled
      const _exhaustive: never = shape;
      throw new Error(`Unhandled shape: ${_exhaustive}`);
  }
}

Control Flow Analysis

TypeScript tracks type narrowing through control flow:

function process(value: string | number | null) {
  if (value === null) {
    return "No value";
  }
  // value is string | number here
  
  if (typeof value === "string") {
    return value.toUpperCase();
  }
  // value is number here
  return value.toFixed(2);
}

Best Practices

  1. Prefer type guards over type assertions
  2. Use discriminated unions with narrowing
  3. Write custom type guards for complex checks
  4. Use assertion functions for validation
  5. Add exhaustiveness checks for union types
  6. Be explicit with null/undefined checks

Common Pitfalls

  1. Don't use truthiness for numbers (0 is falsy)
  2. Remember that typeof null === "object"
  3. Be careful with == vs === in narrowing
  4. Don't forget to check all variants in unions
  5. Avoid overly broad type guards
  6. Watch out for type narrowing in async/callback contexts

Performance Considerations

  • Type guards have no runtime cost (they're just checks)
  • Assertion functions throw errors (consider performance in hot paths)
  • Complex type guards may impact readability
  • Prefer built-in narrowing over custom guards when possible

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