functional-constructs-types

📁 marius-townhouse/effective-typescript-skills 📅 10 days ago
1
总安装量
1
周安装量
#52291
全站排名
安装命令
npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill functional-constructs-types

Agent 安装分布

mcpjam 1
openhands 1
replit 1
windsurf 1
zencoder 1

Skill 文档

Use Functional Constructs and Libraries to Help Types Flow

Overview

Functional programming constructs (map, filter, reduce) work better with TypeScript than imperative loops.

TypeScript’s type inference works particularly well with functional constructs. They produce intermediate types that flow naturally, whereas loops require you to manually track types.

When to Use This Skill

  • Building arrays with for loops
  • Transforming data structures
  • Types not flowing through imperative code
  • Choosing between loops and functional methods

The Iron Rule

Prefer map, filter, and reduce over for loops.
Types flow naturally through functional chains.

Remember:

  • Functional methods return typed values
  • Loops require manual type management
  • Chaining preserves type context
  • Libraries like Lodash have excellent type support

Detection: Loop Type Problems

// Loop: type must be declared or evolves
const result: string[] = [];
for (const item of items) {
  result.push(item.name);
}

// What if you forget the annotation?
const result = [];  // any[]
for (const item of items) {
  result.push(item.name);
}
result
// ^? any[] - type information lost

The Functional Solution

const result = items.map(item => item.name);
// ^? string[] - type inferred automatically

TypeScript infers the output type from the input type and the mapping function.

Type Flow Through Chains

const namesOfAdults = people
  .filter(p => p.age >= 18)
  // ^? Person[]
  .map(p => p.name)
  // ^? string[]
  .sort()
  // ^? string[]
  .join(', ');
  // ^? string

Each step has a well-defined type that TypeScript tracks.

Common Transformations

map: Transform Each Element

const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);
// ^? number[]

const users = [{ name: 'Alice', age: 30 }];
const names = users.map(u => u.name);
// ^? string[]

filter: Keep Elements Matching Condition

const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(n => n % 2 === 0);
// ^? number[]

// With type guard for narrowing
const mixed: (string | number)[] = [1, 'a', 2, 'b'];
const strings = mixed.filter((x): x is string => typeof x === 'string');
// ^? string[]

reduce: Aggregate to Single Value

const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, n) => acc + n, 0);
// ^? number

const grouped = items.reduce((acc, item) => {
  const key = item.category;
  acc[key] = acc[key] || [];
  acc[key].push(item);
  return acc;
}, {} as Record<string, Item[]>);
// Note: reduce sometimes needs type hints

flatMap: Map and Flatten

const nested = [[1, 2], [3, 4], [5]];
const flat = nested.flatMap(arr => arr);
// ^? number[]

const sentences = ['Hello world', 'TypeScript rocks'];
const words = sentences.flatMap(s => s.split(' '));
// ^? string[]

Object Transformations

Object.entries / Object.fromEntries

const obj = { a: 1, b: 2, c: 3 };

// Transform values
const doubled = Object.fromEntries(
  Object.entries(obj).map(([k, v]) => [k, v * 2])
);
// ^? { [k: string]: number }

// Filter entries
const filtered = Object.fromEntries(
  Object.entries(obj).filter(([k, v]) => v > 1)
);

Record Transformations

type Input = Record<string, number>;
type Output = Record<string, string>;

const input: Input = { a: 1, b: 2 };
const output: Output = Object.fromEntries(
  Object.entries(input).map(([k, v]) => [k, String(v)])
);

Lodash and Type-Friendly Libraries

import _ from 'lodash';

const grouped = _.groupBy(users, 'department');
// ^? Dictionary<User[]>

const sorted = _.sortBy(users, ['lastName', 'firstName']);
// ^? User[]

const unique = _.uniqBy(users, 'id');
// ^? User[]

Lodash has excellent TypeScript support.

When Loops Are OK

Performance-Critical Code

// Loop might be faster for very large arrays
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
  sum += numbers[i];
}

Early Exit

// find is functional, but loops can break early
function findFirst<T>(arr: T[], pred: (x: T) => boolean): T | undefined {
  for (const item of arr) {
    if (pred(item)) return item;
  }
  return undefined;
}
// Or just use: arr.find(pred)

Complex Mutations

// Some algorithms are clearer with loops
function quickSort<T>(arr: T[]): T[] {
  // ... loop-based implementation
}

Converting Loops to Functional

// Before: loop with accumulator
const result: ProcessedItem[] = [];
for (const item of items) {
  if (item.isValid) {
    result.push(processItem(item));
  }
}

// After: filter + map
const result = items
  .filter(item => item.isValid)
  .map(item => processItem(item));

Pressure Resistance Protocol

1. “Loops Are More Readable”

Pressure: “I understand for loops better”

Response: Functional methods express intent clearly: map = transform, filter = select, reduce = aggregate.

Action: Learn the patterns. They become natural quickly.

2. “Performance Concerns”

Pressure: “Multiple passes are slower”

Response: For most data sizes, clarity beats micro-optimization.

Action: Profile before optimizing. Most code isn’t performance-critical.

Red Flags – STOP and Reconsider

  • const result = [] followed by loop pushing elements
  • Type annotations needed only because of loops
  • Complex state tracking in loops
  • any[] that should be more specific

Common Rationalizations (All Invalid)

Excuse Reality
“Loops are simpler” Functional methods have clearer intent
“I need the index” .map((item, i) => ...) provides index
“Multiple passes are slow” Usually doesn’t matter; measure first

Quick Reference

// DON'T: Loop with manual type
const result: string[] = [];
for (const x of items) {
  result.push(x.name);
}

// DO: Functional with inferred type
const result = items.map(x => x.name);

// Filter + Map
const processed = items
  .filter(x => x.isValid)
  .map(x => transform(x));

// Type guard in filter
const strings = mixed.filter((x): x is string => typeof x === 'string');

// Reduce (with type hint when needed)
const grouped = items.reduce((acc, x) => ..., {} as GroupedType);

The Bottom Line

Functional constructs make types flow naturally.

map, filter, reduce, and similar methods produce well-typed results without manual annotation. They express transformations clearly and work excellently with TypeScript’s inference. Use loops only when you have a specific reason to.

Reference

Based on “Effective TypeScript” by Dan Vanderkam, Item 26: Use Functional Constructs and Libraries to Help Types Flow.