I use TypeScript every day. I mess up, too. One tiny thing keeps biting me: what happens when I don’t set a type parameter. You know what? TypeScript is helpful most days. But it can also be sneaky. (I collected an even deeper dive in this dedicated post on what happens when you forget the type param if you want the full story.)
Here’s my honest take, with real code I wrote at work and at home.
Quick take: what I see when I skip the type
- TypeScript tries to guess from your input. Good when it’s clear.
- If it can’t guess, it often gives you unknown. Sometimes any. Sometimes never.
- Some libs add their own defaults. That can hide bugs. Or save your bacon.
Let me explain with simple cases.
For an expanded guide with more production examples, head over to my write-up on ImprovingCode.
When TypeScript can guess, you’re fine
If the function has a value it can read, it will infer the type.
function id<T>(value: T): T {
return value;
}
const a = id(42); // number
const b = id("pizza"); // string
const c = id({ ok: true }); // { ok: boolean }
No type param set. Still works. Clean and sweet.
When it can’t guess, you get unknown
This is the one that trips folks.
function makeValue<T>() {
// pretend we're building something complex
return null as unknown as T;
}
const value = makeValue(); // type: unknown
// value.toUpperCase(); // error, it's unknown
No input. No hint. So T becomes unknown. That’s pretty strict. It keeps you safe, but it might slow you down.
Empty arrays can produce never (yep, never)
This one feels weird the first time.
function first<T>(xs: T[]): T | undefined {
return xs[0];
}
const f1 = first([1, 2, 3]); // number | undefined
const f2 = first([]); // never | undefined
An empty array gives no clues. So it infers T as never. That reads like, “there is no possible item here.” Fair point. But it can shock you during refactors.
Tip: give the array a type.
const f3 = first<number>([]); // number | undefined
// or
const f4 = first([] as number[]);
If you later iterate over such arrays, TypeScript’s quirks around forEach can show up, too—my hands-on take on the forEach loop and my follow-up with an even more honest take cover the edge cases.
Default type parameters save the day
You can set a default type on your own generics. It helps a lot.
function wrap<T = string>(value?: T) {
return { value } as { value: T };
}
const w1 = wrap(); // { value: string | undefined }
const w2 = wrap(123); // { value: number }
const w3 = wrap<boolean>(true); // { value: boolean }
No type passed? It uses string (in this case). Clear and steady. (Introduced in TypeScript 2.3—see the official release notes for details.)
Real library stuff I hit in the wild
Little tour of “I didn’t set the type and here’s what I got.”
-
React useState
const [thing, setThing] = useState(); // thing: undefined // Type default comes from React’s types: S = undefined when no initial value.If you want null instead:
const [user, setUser] = useState<null | { id: string }>(null); -
Promise.resolve with no value
const p = Promise.resolve(); // Promise<void>Handy for chaining, but it’s not “any.” It’s void.
-
Map and Set with no entries
const m = new Map(); // Map<any, any> const s = new Set(); // Set<any>These default to any. It’s easy, but also risky. I try to type them:
const users = new Map<string, number>(); -
Axios (no generic)
const res = await axios.get("/api/user"); // AxiosResponse<any> const user = res.data; // anyIf you want safety:
type User = { id: string; name: string }; const res2 = await axios.get<User>("/api/user"); const user2 = res2.data; // User -
fetch + Response.json
const res = await fetch("/api/user"); const data = await res.json(); // anyYou can guide it:
const data2 = await res.json<{ id: string; name: string }>(); // typed -
Prefer constructors with named arguments
If you like a more explicit API, you can emulate named-argument constructors—I shared the pattern in this experiment with TypeScript named arguments.
A tiny constraint helps a lot
I like soft rails. Not walls.
function readField<T extends Record<string, unknown>>(obj: T, key: keyof T) {
return obj[key];
}
const val = readField({ a: 1 }, "a"); // number
// Without the constraint, you can end up with unknown or any more often.
While we’re on safe refactors, keeping JSDoc in sync when you rename a field is another life-saver—here’s how I handle it in practice.
Common gotchas I’ve hit
- Empty arrays infer never. Use a type.
- No inputs and no defaults? You get unknown.
- Some stdlib constructors return any when empty (Map, Set).
- Union types can widen if you’re not careful with literals.
const x = Math.random() < 0.5 ? "a" : "b"; // "a" | "b" id(x); // still "a" | "b" — nice id(["a", "b"]); // string[] (widened) unless you use const const xs = ["a", "b"] as const; // readonly ["a","b"] - CLI helpers that take a file-path argument often default to
string, but you can give them a more specific type—my take on typing a file path argument shows the pattern.
My simple rules now
- If the call has no clear input, add the generic or a default.
- Type empty things: [], new Map(), new Set().
- Use defaults on your APIs: <T = MyDefault>.
- Keep noImplicitAny on. It catches leaks.
- For data from the network, set the type near the edge.
- Write useful comments that survive the compiler—my notes on real-world TypeScript comments explain why.
Final verdict
I like how TypeScript guesses. It feels smart. But when I skip a type parameter, I can get unknown, never, or any, based on the shape of the call and the library types. That mix is both helpful and sneaky.
Would I trust inference alone? Not always. I enjoy the speed, but I set defaults on my own generics, and I annotate the tricky spots. It’s a small habit that saves a big mess later.
In the same spirit of choosing sensible defaults, sometimes you also want a modern alternative to the default classifieds sites you use online. If Craigslist isn’t quite meeting your needs, an increasingly popular option is Doublelist—the linked guide walks you through how the platform compares to traditional personals sites, outlines its safety features, and offers practical tips for creating effective listings and meeting people locally.
If your travels or home base land you along South Carolina’s Grand Strand, you may prefer a service that focuses specifically on that area’s nightlife and dating scene. A quick read of the AdultLook Myrtle Beach guide will give you a clear overview of how the site works, current pricing, safety best practices, and insider tips for arranging low-stress meet-ups while you’re in town.
And
