mcpField
P
precise-any-variants

precise-any-variants

github.com/marius-townhouseDevelopment6151 installsUpdated 40w ago
Repository

About precise-any-variants

precise-any-variants is a TypeScript guidance skill for choosing narrower alternatives when `any` cannot be avoided. It helps developers retain some type checking, and explains when `unknown` is safer.

Key features

  1. 01Recommends specific forms such as `any[]`
  2. 02Uses `Record` for object inputs
  3. 03Uses function types for callable inputs
  4. 04Explains safer use of `unknown`
  5. 05Shows TypeScript examples and pitfalls

Use cases

  1. 01Typing functions that accept dynamic data
  2. 02Migrating code from JavaScript
  3. 03Reviewing over-broad `any` annotations
  4. 04Parsing JSON with safer types

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill precise-any-variants

After installing, type/precise-any-variantsin Claude Code to confirm it loaded.

Details

From the project README

Prefer More Precise Variants of any to Plain any

Overview

If you must use any, make it as specific as possible.

Plain any accepts everything. But any[], Record<string, any>, or () => any are narrower and still provide some type checking.

When to Use This Skill

  • Forced to use any for some reason
  • Writing functions that accept "anything"
  • Dealing with truly dynamic data
  • Migrating from JavaScript

The Iron Rule

any is a last resort.
Specific variants of any preserve partial type safety.

Remember:

  • any[] checks that it's an array
  • Record<string, any> checks that it's an object
  • () => any checks that it's a function
  • unknown is even safer than any variant

Detection: Over-broad any

function getLength(x: any) {  // Too broad!
  return x.length;
}

getLength(123);     // No error, crashes at runtime
getLength(null);    // No error, crashes at runtime
getLength([1,2,3]); // OK

Better: Specific any Variants

any[] for Arrays
function getLength(array: any[]) {
  return array.length;  // Type checked!
}

getLength([1, 2, 3]);  // OK
getLength(/regex/);
//        ~~~~~~~
// Argument of type 'RegExp' is not assignable to parameter of type 'any[]'

Benefits:

  • .length access is type-checked
  • Return type is number, not any
  • Non-arrays are rejected
Record<string, any> for Objects
function hasKey(obj: Record<string, any>, key: string): boolean {
  return key in obj;
}

hasKey({ a: 1 }, 'a');  // OK
hasKey(null, 'a');
//     ~~~~
// Argument of type 'null' is not assignable to parameter of type 'Record<string, any>'
() => any for Functions
type Fn0 = () => any;           // No params
type Fn1 = (arg: any) => any;   // One param
type FnN = (...args: any[]) => any;  // Any params

function callTwice(fn: FnN) {
  fn();
  fn();
}

callTwice(() => console.log('hi'));  // OK
callTwice(123);
//        ~~~
// Argument of type 'number' is not assignable to parameter of type '(...args: any[]) => any'

Use any[] for Rest Parameters

// any rest parameter: return type is any
const numArgsBad = (...args: any) => args.length;
//    ^? (...args: any) => any

// any[] rest parameter: return type is number
const numArgsBetter = (...args: any[]) => args.length;
//    ^? (...args: any[]) => number

The return type matters for downstream code!

Comparison of any Variants

TypeAcceptsRejects
anyEverythingNothing
any[]ArraysNon-arrays
Record<string, any>ObjectsPrimitives, null
() => anyFunctionsNon-functions
objectObjects, arraysPrimitives, null
unknownEverythingEverything (without check)

Consider unknown Instead

unknown is even safer:

function process(data: unknown) {
  // Must narrow before using
  if (Array.isArray(data)) {
    data.length;  // OK, data is any[]
  }
  if (typeof data === 'object' && data !== null) {
    // data is object
  }
}

With unknown, you can't do anything without first checking the type.

Real-World Example: JSON Parsing

// Don't return any
function parseBad(json: string): any {
  return JSON.parse(json);
}

// Better: return unknown
function parseGood(json: string): unknown {
  return JSON.parse(json);
}

// Caller must narrow:
const data = parseGood('{"x": 1}');
if (typeof data === 'object' && data !== null && 'x' in data) {
  console.log(data.x);
}

When any is Unavoidable

Sometimes you genuinely need any:

// Wrapping a library that uses any internally
function wrapLibrary<T>(input: T): T {
  return (library as any).process(input);
}

// Type assertion in implementation
function merge<T>(a: Partial<T>, b: Partial<T>): T {
  return { ...a, ...b } as any as T;
}

Even then, hide it inside functions with good type signatures (Item 45).

Pressure Resistance Protocol

1. "any Works"

Pressure: "Just use any and move on"

Response: any disables ALL type checking. Specific variants preserve some safety.

Action: Use the most specific variant that works.

2. "I Don't Know the Type"

Pressure: "The type is truly dynamic"

Response: unknown is safer for truly unknown types.

Action: Use unknown, then narrow before using.

Red Flags - STOP and Reconsider

  • any as function parameter (use specific variant)
  • any as return type (prefer unknown)
  • any for objects (use Record<string, any> or object)
  • any for arrays (use any[])

Common Rationalizations (All Invalid)

ExcuseReality
"It's too dynamic to type"unknown handles truly dynamic data
"Specific variants are verbose"A few characters save runtime errors
"any is fine for internal code"Internal code still has bugs

Quick Reference

// DON'T: Plain any
function f(x: any): any { ... }

// DO: Specific variants
function getLength(arr: any[]): number { ... }
function getKeys(obj: Record<string, any>): string[] { ... }
function call(fn: (...args: any[]) => any): void { ... }

// BEST: unknown when possible
function process(data: unknown): void {
  if (Array.isArray(data)) { ... }
}

The Bottom Line

If you must use any, make it specific.

any[], Record<string, any>, and () => any preserve some type checking while still allowing flexibility. But consider whether unknown is a safer choice.

Reference

Based on "Effective TypeScript" by Dan Vanderkam, Item 44: Prefer More Precise Variants of any to Plain any.

Comments

Markdown supported