TypeScript Type Errors skill for Codex

Decode the error, fix the cause, skip the any. Codex reads the compiler's elaboration chain from the bottom, probes the real types involved, and decides whether the value, the annotation, the library or the config is at fault before editing. It shrinks stubborn errors to a minimal repro, consults a bundled catalog of error codes for assignability, overload, module resolution and declaration emit failures, and refuses any, double casts and ts-ignore as fixes.

  • Languages
  • $typescript-type-errors
  • 164 lines
  • 3 files
  • ~2,000 tokens
  • MIT
  • Updated
  • Editor's pick

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- typescript-type-errors

Installs to ~/.agents/skills/typescript-type-errors/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $typescript-type-errors, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Diagnoses and fixes hard TypeScript compiler errors without any, @ts-ignore or double casts - reads elaboration chains bottom-up, reduces errors to minimal repros, and resolves assignability, overload, excessively deep instantiation, module resolution and declaration emit errors such as TS2322, TS2345, TS2769, TS2589, TS2307, TS7016 and TS2742. Use when tsc or the editor shows an error the user cannot decode, a library upgrade breaks types, or someone proposes as any to make it compile.

Use it when

  • A TypeScript error message is long, nested or contradictory
  • A dependency upgrade broke compilation
  • Module resolution or declaration emit errors block the build
  • Someone suggests as any or ts-ignore to get a green build

Use something else when

How to check the result

The project's real typecheck command exits 0, the diff adds no any, double casts or suppressions, and the root cause is stated in one sentence.

The mistake it prevents

Fixing the outermost line of an error message, when the actual mismatch is described on the last line of the elaboration chain.

~/.agents/skills/typescript-type-errors/SKILL.md

--- name: typescript-type-errors description: Diagnoses and fixes hard TypeScript compiler errors without any, @ts-ignore or double casts - reads elaboration chains bottom-up, reduces errors to… license: MIT ---

Done well means: the root cause is named in one sentence, the fix changes the type or the code that was actually wrong, and no any, as unknown as, @ts-ignore or non-null assertion was added to get a green build.

Workflow

- [ ] 1. Get the exact error: code, file, line, full message (not the IDE's truncated hover)
- [ ] 2. Read the elaboration chain bottom-up; the last line is usually the real mismatch
- [ ] 3. Inspect the actual types involved (hover, a probe type, or tsc --declaration)
- [ ] 4. Decide who is wrong: the value, the annotation, the library types, or the config
- [ ] 5. Reduce to a minimal repro if the cause is still unclear after 10 minutes
- [ ] 6. Fix at the source; re-run tsc; confirm no new suppressions
npx tsc --noEmit --pretty false 2>&1 | head -50          # plain, greppable output
npx tsc --noEmit --pretty false | grep -o 'error TS[0-9]*' | sort | uniq -c | sort -rn   # error histogram
npx tsc --noEmit --noErrorTruncation                     # full types in messages

Read AGENTS.md for the real typecheck command; monorepos often use tsc -b, vue-tsc or per-package scripts, and running plain tsc at the root can check the wrong config.

Reading the message

TypeScript reports the outermost failing assignment first, then elaborates:

error TS2345: Argument of type '{ id: string; tags: string[]; }' is not assignable to parameter of type 'Post'.
  Types of property 'tags' are incompatible.
    Type 'string[]' is not assignable to type 'readonly Tag[]'.
      Type 'string' is not assignable to type 'Tag'.

The last line is the fact to act on: a plain string is being used where a Tag union literal is expected. Fix the producer (as const, a typed constant, or validation that returns Tag), not the outer call.

Inspecting types

type Probe = ReturnType<typeof buildQuery>;   // hover Probe in the editor
const _check: Expected = actualValue;         // forces a precise assignability error
declare const x: Parameters<typeof fn>[1];    // see what an argument really expects
  • // ^? twoslash queries work in the TS playground and some editors to print a type inline.
  • For library types, open the .d.ts that tsc --traceResolution shows was actually loaded, not the one you assume. Two copies of the same package produce “Type X is not assignable to type X” errors.

Decision table: whose fault is it

EvidenceRoot causeFix
Value’s inferred type is wider than intended (string vs literal)Wideningas const, satisfies, explicit annotation on the declaration
Annotation is narrower than the data really isWrong declared typeWiden the type honestly and handle the new cases
Value is genuinely invalid (missing field, wrong shape)Real bugFix the code; this is the compiler working
Error appeared after upgrading a packageLibrary types changedRead the changelog; adapt call sites; pin only as a stopgap
Error mentions two identical-looking typesDuplicate package copies or declaration mergingnpm ls <pkg> / pnpm why <pkg>; dedupe
Module or declaration not foundConfig or packagingmoduleResolution, types, paths, package exports (see catalog)
Error only in one tool (IDE vs CLI, tsc vs vue-tsc)Different tsconfig or TS versionCheck which config and version each tool uses

Common hard errors, short form

CodeMeaningUsual real fix
TS2322 / TS2345Not assignableRead chain bottom-up; fix widening or the wrong annotation
TS2769No overload matchesRead the error for the last overload; often the closest; check argument order and callback shapes
TS2339Property does not existNarrow the union first (in, discriminant, type guard)
TS18048 / TS2532Possibly undefinedNarrow or default; see $typescript-strict-mode
TS2589Instantiation excessively deepSimplify recursive types; add explicit type arguments to break inference loops
TS2307 / TS7016Module or its types not foundInstall @types/x, fix exports/types, or add a minimal declare module
TS2742Inferred type cannot be named without a referenceAdd an explicit return type annotation using exported types
TS2835Relative import needs extension (node16/nodenext)Add .js to the import specifier

The full catalog with examples, including declaration emit, module format and JSX errors, is in references/error-codes.md. Read it when the code you see is not in the table above or the short fix did not work.

Narrowing instead of casting

type Result = { ok: true; data: User } | { ok: false; error: string };

function handle(r: Result) {
  if (!r.ok) return showError(r.error);   // discriminant narrows
  render(r.data);
}

function isUser(v: unknown): v is User {
  return typeof v === "object" && v !== null && "id" in v && typeof (v as { id: unknown }).id === "string";
}

Since TS 5.5, simple filter predicates infer automatically: items.filter(x => x !== undefined) yields T[], not (T | undefined)[].

When a cast is legitimate

  • After runtime validation the compiler cannot follow (schema parse already returns the typed value, so prefer that).
  • At a boundary with incorrect third-party types, with a comment linking the upstream issue.
  • Building an object incrementally inside a function ({} as Partial<T> then completed).

Even then: single as T with a comment, never as unknown as T across unrelated types, never any. Prefer @ts-expect-error <reason> over @ts-ignore when a suppression is unavoidable, so it fails once the underlying error is fixed.

Minimal repro

When the cause is still unclear, copy the failing expression into a scratch file and delete everything not needed to reproduce the error: imports become local declare statements, large interfaces shrink to the two relevant properties. Usually the cause becomes obvious by step 5. If it does not, the repro is ready for an upstream issue or a TS playground link.

// scratch/repro.ts
declare function useQuery<T>(opts: { queryFn: () => Promise<T>; select?: (d: T) => unknown }): T;
const r = useQuery({ queryFn: async () => [1, 2], select: d => d.length });

Run npx tsc --noEmit --strict scratch/repro.ts. Delete the scratch file afterwards.

npx tsc --noEmit --extendedDiagnostics | grep -E 'Instantiations|Check time|Memory'
npx tsc --noEmit --generateTrace trace/   # then: npx @typescript/analyze-trace trace (needs network once)

Hot spots are usually one recursive utility type or a giant union. Fix the type, then compare instantiation counts before and after.

Gotchas

  • Fixing the first error in a file often removes ten cascading ones. Re-run tsc after each real fix instead of working through the whole list blindly.
  • // @ts-expect-error on a line with multiple errors hides all of them.
  • IDE shows an error tsc does not: the editor uses a different TS version (“Select TypeScript Version” in VS Code) or a different tsconfig for that file. Since TypeScript 7, tools built on the compiler API (vue-tsc, type-aware typescript-eslint rules) may run a 6.x compiler next to the native 7.x tsc, so their errors can differ.
  • Errors in node_modules/**/*.d.ts with skipLibCheck: false are often mismatched @types/* versions; align them before editing anything.
  • declare module "x" with no body types everything as any; it hides errors rather than fixing them. Write the few signatures you actually use.

Verification

  • tsc --noEmit with the project’s real command exits 0.
  • git diff adds no any, as unknown as, @ts-ignore, or ! assertions; any single as has a comment.
  • Explain the root cause in one sentence in the PR or reply, for example: “status widened to string because the config object was not as const”.

What the zip contains

Codex reads bundled files only when a step points to them. Every one is shown here, so you can review it before installing.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "TypeScript Type Errors"
  short_description: "Decode the error, fix the cause, skip the any"
  default_prompt: "$typescript-type-errors What does this TS2769 no overload matches error actually mean?"
references/error-codes.md160 lines, 8.1 KB
# TypeScript error catalog

Read the section matching the error code. Each entry: what the compiler is saying, the
usual root causes, and fixes that keep the code type-safe. Codes are stable across
TypeScript 5.x to 7.x; exact wording can differ slightly in the 7.x native compiler.

## Assignability

### TS2322 Type 'A' is not assignable to type 'B'
- Literal widening: `let mode = "dark"` is `string`. Use `const`, `as const`, or annotate the
  declaration with the union type.
- Object literal freshness: extra properties are only an error on fresh literals (TS2353).
  Assigning through a variable hides them; that is not a fix.
- Readonly arrays: `readonly T[]` is not assignable to `T[]`. Change the parameter to accept
  `readonly T[]` if it does not mutate.
- Function variance: callback parameter types must accept at least what the caller passes.

### TS2345 Argument of type 'A' is not assignable to parameter of type 'B'
Same as TS2322 at a call site. Check whether a generic inferred from another argument
(use `NoInfer` on the parameter that should not drive inference).

### TS2353 Object literal may only specify known properties
Typo or stale field name in a literal. If the property is legitimately extra, the target
type is missing it; add it there.

### TS2741 Property 'x' is missing in type 'A' but required in type 'B'
Either supply it, or the target type should mark it optional. Check whether the value came
from a `Partial<>` or a spread that dropped it.

### TS2367 This comparison appears to be unintentional because the types have no overlap
Usually a real bug (comparing enum to string, or a narrowed variable to a value it cannot
hold anymore). Re-read the control flow above the comparison.

### TS2416 Property 'x' in type 'Sub' is not assignable to the same property in base type
Override signature is incompatible. Accept the base parameter types (or wider) and return
the base return type (or narrower). Add `override` with `noImplicitOverride`.

## Overloads and calls

### TS2769 No overload matches this call
The message lists the failure for each overload; the last listed is not always the most
relevant. Find the overload you intended and read its elaboration. Common causes: a callback
parameter typed too narrowly, an options object with one wrong field, React event handler
types (`ChangeEvent<HTMLInputElement>` vs `HTMLSelectElement`).

### TS2554 Expected N arguments, but got M
Signature changed after an upgrade, or `this`-binding confusion with methods passed as
callbacks. For optional trailing parameters, check `exactOptionalPropertyTypes`.

### TS2349 This expression is not callable
Union of function types with incompatible parameters, or a namespace import used as a
function (`import * as x` where the module exports a default function; use a default
import with `esModuleInterop`).

## Narrowing and unknown

### TS2339 Property 'x' does not exist on type 'A | B'
Narrow before access: discriminant check, `"x" in value`, `instanceof`, or a type guard.

### TS18046 'x' is of type 'unknown'
Catch variables under `useUnknownInCatchVariables`, or `JSON.parse` results passed through
`unknown`. Narrow with `instanceof`, a schema parse, or a type guard.

### TS18047 / TS18048 / TS2531 / TS2532 possibly null or undefined
Narrow, provide a default with `??`, or make the type honest at its source.

### TS7053 Element implicitly has an 'any' type because expression of type 'string' can't be used to index
The key is `string` but the object has known keys. Type the key as `keyof typeof obj`, use
a `Record<string, V>` or `Map` for dynamic keys, or check with `key in obj` plus a guard.

## Implicit any

### TS7006 / TS7031 Parameter implicitly has an 'any' type
Annotate the parameter. For callbacks, contextual typing usually works if the outer function
is typed; the error means it is not.

### TS7005 / TS7034 Variable implicitly has type 'any'
`let x;` assigned in branches. Declare the type: `let x: Foo | undefined;`.

## Generics and complexity

### TS2589 Type instantiation is excessively deep and possibly infinite
A recursive conditional or mapped type that does not terminate quickly enough, often
triggered by inference over a large schema (ORMs, validators, router trees). Fixes: pass
explicit type arguments at the call site to skip inference, break the type into named
intermediate aliases, make recursion tail-recursive, or cap depth with a counter tuple.

### TS2590 Expression produces a union type that is too complex to represent
Template literal or distributive conditional multiplying unions (limit ~100,000 members).
Constrain inputs, or stop distribution with `[T] extends [U]`.

### TS2344 Type 'X' does not satisfy the constraint 'Y'
The type argument does not meet `extends`. Either the constraint is too strict or the
argument is wrong; check the generic's declaration.

### TS2775 Assertions require every name in the call target to be declared with an explicit type annotation
Assertion functions (`asserts x is T`) must be called through an explicitly typed reference.
Annotate the variable holding the function: `const assertUser: (v: unknown) => asserts v is User = ...`.

## Modules and resolution

### TS2307 Cannot find module 'x' or its corresponding type declarations
Check, in order: the package is installed; `moduleResolution` matches the runtime
(`bundler` for Vite/webpack apps, `nodenext` for Node); the package's `exports` map has a
`types` condition for the entry you import; `paths` aliases match the bundler aliases.
Debug with `npx tsc --traceResolution | grep -A5 "'x'"`.

### TS7016 Could not find a declaration file for module 'x'
The package ships no types. Install `@types/x` if it exists; otherwise add
`src/types/x.d.ts` with `declare module "x" { export function used(a: string): number; }`
declaring only what you call.

### TS2835 Relative import paths need explicit file extensions
Under `module: node16`/`nodenext` in ESM files, write `./util.js` (even for `.ts` sources).
With TS 5.7+, `rewriteRelativeImportExtensions` allows `.ts` specifiers when emitting.

### TS1479 The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module
A CJS file imports an ESM-only package. Convert the file to ESM, use dynamic `import()`,
or on Node 20.19+/22.12+ with `module: nodenext` rely on `require(esm)` support where TS allows it.

### TS1259 Module can only be default-imported using the 'esModuleInterop' flag
Enable `esModuleInterop` (and `allowSyntheticDefaultImports`), or use `import x = require("x")`
in CJS-targeted code.

### TS1286 / TS1484 import type / verbatimModuleSyntax errors
With `verbatimModuleSyntax`, type-only imports must be written `import type { X }`, and
ESM syntax is not allowed in files that compile to CommonJS.

## Declaration emit

### TS2742 The inferred type of 'x' cannot be named without a reference to 'y'
Emitting `.d.ts` needs a type that lives in a non-exported or deep-path module. Add an
explicit return type using types exported from the package's public entry, or export the
internal type.

### TS4023 / TS4058 / TS4060 Exported variable or return type has or is using private name
Same family: export the type that the public signature mentions.

### TS90xx errors under isolatedDeclarations
With `isolatedDeclarations`, exported functions and values need explicit types so other
tools can emit `.d.ts` without type inference. Annotate return types and exported constants.

## Classes

### TS2564 Property has no initializer and is not definitely assigned in the constructor
Initialize it, make it optional, or use `declare` for fields set by frameworks.

### TS2611 / TS2610 property vs accessor mismatch
Base class defines a property and subclass an accessor (or vice versa), common after
`useDefineForClassFields` changes. Use the same member kind as the base.

## JSX and React

### TS2786 'X' cannot be used as a JSX component
Duplicate `@types/react` versions in the tree, or the component returns a type not
assignable to `ReactNode`. Dedupe `@types/react`.

### TS2322 on JSX props with `IntrinsicAttributes`
The component's props type does not include what you passed. Check generic components
need explicit props generics or a `<T,>` arrow form.

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc