Loading...
Loading...
Use when writing multiple functions with same signature. Use when implementing callbacks. Use when matching existing function types.
npx skill4agent add marius-townhouse/effective-typescript-skills function-type-expressionsWhen functions share a signature, define the type ONCE and apply it to EACH function.typeof fn// Repetitive - same signature 4 times
function add(a: number, b: number) { return a + b; }
function sub(a: number, b: number) { return a - b; }
function mul(a: number, b: number) { return a * b; }
function div(a: number, b: number) { return a / b; }type BinaryFn = (a: number, b: number) => number;
const add: BinaryFn = (a, b) => a + b; // Types inferred
const sub: BinaryFn = (a, b) => a - b;
const mul: BinaryFn = (a, b) => a * b;
const div: BinaryFn = (a, b) => a / b;// Match fetch's signature exactly
const checkedFetch: typeof fetch = async (input, init) => {
const response = await fetch(input, init);
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return response;
};input: RequestInfo | URLinit?: RequestInitPromise<Response>// Match parameters but change return type
async function fetchNumber(
...args: Parameters<typeof fetch>
): Promise<number> {
const response = await checkedFetch(...args);
return Number(await response.text());
}// Statement - must annotate each parameter
function rollDice1(sides: number): number { /* ... */ }
// Expression - can apply type to entire function
type DiceRollFn = (sides: number) => number;
const rollDice2: DiceRollFn = (sides) => { /* ... */ };type EventHandler = (event: Event) => void;
type AsyncCallback<T> = () => Promise<T>;
type Comparator<T> = (a: T, b: T) => number;
const handleClick: EventHandler = (e) => {
console.log(e.target); // e is typed as Event
};type Mapper<T, U> = (item: T, index: number) => U;
const double: Mapper<number, number> = (n) => n * 2;
const stringify: Mapper<number, string> = (n) => String(n);// Function type as interface
interface StringTransform {
(input: string): string;
}
const toUpper: StringTransform = s => s.toUpperCase();const checkedFetch: typeof fetch = async (input, init) => {
const response = await fetch(input, init);
if (!response.ok) {
return new Error('Failed'); // Error!
// Type 'Error' is not assignable to type 'Response'
}
return response;
};// React provides these
import { MouseEventHandler, ChangeEventHandler } from 'react';
const handleClick: MouseEventHandler<HTMLButtonElement> = (e) => {
console.log(e.currentTarget.disabled); // Fully typed
};
const handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
console.log(e.target.value); // Fully typed
};// Overkill for a single standalone function
type GreetFn = (name: string) => string;
const greet: GreetFn = (name) => `Hello, ${name}`;
// Just use a normal function statement
function greet(name: string): string {
return `Hello, ${name}`;
}typeof existingFunctionParameters<typeof fn>| Excuse | Reality |
|---|---|
| "Types on parameters are clearer" | Not when repeated 4 times |
| "Function statements are simpler" | Function expressions with types are just as clear |
"I'll just use | Loses all type safety benefits |
// Define function type
type Transform<T> = (value: T) => T;
// Apply to function expression
const double: Transform<number> = v => v * 2;
// Match existing function
const myFetch: typeof fetch = async (input, init) => { ... };
// Match parameters, change return
function wrapper(...args: Parameters<typeof original>): NewReturn { ... }typeof