Question
I am testing my UserRouter and loading user data from a JSON file.
data.json
[
{
"id": 1,
"name": "Luke Cage",
"aliases": ["Carl Lucas", "Power Man", "Mr. Bulletproof", "Hero for Hire"],
"occupation": "bartender",
"gender": "male",
"height": {
"ft": 6,
"in": 3
},
"hair": "bald",
"eyes": "brown",
"powers": [
"strength",
"durability",
"healing"
]
}
]
When I build the app, I get this TypeScript error:
ERROR in .../UserRouter.ts
(30,27): error TS7006: Parameter 'user' implicitly has an 'any' type.
UserRouter.ts
import { Router, Request, Response, NextFunction } from 'express';
const Users = require('../data');
export class UserRouter {
router: Router;
constructor() {
// ...
}
/**
* GET one User by id
*/
public getOne(req: Request, res: Response, _next: NextFunction) {
let query = parseInt(req.params.id);
let user = Users.find(user => user.id === query);
if (user) {
res.status(200).send({
message: 'Success',
status: res.status,
user
});
} else {
res.status(404).send({
message: 'No User found with the given id.',
status: res.status
});
}
}
}
const userRouter = new UserRouter().router;
export default userRouter;
Why does TypeScript report that the user parameter implicitly has type any, and how should this code be typed correctly?
Short Answer
By the end of this page, you will understand what the TypeScript error TS7006 means, why it often appears when loading JSON or untyped modules, and how to fix it by defining interfaces and giving arrays explicit types. You will also see how this affects callback parameters like the user inside .find().
Concept
TypeScript tries to understand the type of every variable, function parameter, and return value. The error TS7006: Parameter 'xxx' implicitly has an 'any' type appears when TypeScript cannot figure out a parameter's type and your project does not allow silent any values.
In your example, the important part is this line:
let user = Users.find(user => user.id === query);
The callback parameter user gets its type from the type of Users. If Users is properly typed as something like User[], then TypeScript knows that user is a User. But if Users comes from require('../data') without a clear type, TypeScript may treat it as any, or fail to infer the callback parameter type safely.
This matters because TypeScript's main benefit is catching mistakes before runtime. If everything becomes any, TypeScript cannot help you detect problems like:
- misspelled property names
- wrong data shapes
- invalid comparisons
- missing nested fields
The real fix is usually not to manually type the callback parameter first. The better fix is to type the data source correctly so TypeScript can infer the callback parameter automatically.
For this data, a proper solution is to define a User interface and make sure Users is typed as User[].
Mental Model
Think of TypeScript like a warehouse labeling system.
- A
Userinterface is the label describing what each box contains. - A
User[]is a shelf full of those labeled boxes. .find(user => ...)means: "look through each box on the shelf."
If the shelf has no label, TypeScript does not know what kind of boxes are on it. Then when you say user.id, it cannot safely tell what user is supposed to be.
So the problem is usually not the worker checking each box (user => ...). The problem is that the shelf (Users) was never labeled clearly.
Syntax and Examples
The core idea is to define your data shape and use it everywhere.
Define an interface
interface Height {
ft: number;
in: number;
}
interface User {
id: number;
name: string;
aliases: string[];
occupation: string;
gender: string;
height: Height;
hair: string;
eyes: string;
powers: string[];
}
Type an array of users
const users: User[] = [
{
id: 1,
name: 'Luke Cage',
aliases: ['Carl Lucas', 'Power Man'],
occupation: 'bartender',
gender: 'male',
height: { ft: 6, in: 3 },
hair: 'bald',
eyes: 'brown',
powers: ['strength', 'durability', 'healing']
}
];
Now .find() works with inference:
const result = users.find(user => user.id === 1);
Here, TypeScript knows:
usersisUser[]- each
userinside.find()is aUser resultisUser | undefined
Applying it to imported JSON
A common pattern is:
import rawUsers from '../data.json';
const users: User[] = rawUsers;
Then use:
const user = users.find(user => user.id === query);
You can also type the callback parameter directly
const user = users.find((user: User) => user.id === query);
This works, but it is usually better to type users correctly and let TypeScript infer the callback parameter automatically.
Step by Step Execution
Consider this small example:
interface User {
id: number;
name: string;
}
const users: User[] = [
{ id: 1, name: 'Luke Cage' },
{ id: 2, name: 'Jessica Jones' }
];
const query = 2;
const foundUser = users.find(user => user.id === query);
Here is what happens step by step:
-
TypeScript reads the
Userinterface.- A
Usermust haveidandname.
- A
-
TypeScript reads
const users: User[].- This means
usersis an array ofUserobjects.
- This means
-
TypeScript reads
.find(user => user.id === query).- Because
usersisUser[], each item in the array is aUser. - So the callback parameter
useris automatically inferred asUser.
- Because
-
TypeScript checks
user.id.- This is valid because
idexists onUser.
- This is valid because
-
TypeScript computes the result type.
.find()may return a matchingUser.- If nothing matches, it returns
undefined. - So
foundUserhas typeUser | undefined.
If users had no type, then TypeScript might not know what user is, which leads to weaker checking or errors like TS7006.
Real World Use Cases
This concept appears often in real projects whenever data comes from outside your code.
Common situations
-
Reading JSON files
- seed data
- test fixtures
- local configuration
-
API responses
- user lists from a backend
- product data from a service
- analytics records
-
Database query results
- rows returned by an ORM or query builder
-
Express route handlers
- searching, filtering, and validating records before sending a response
Example: filtering API data
interface Product {
id: number;
name: string;
price: number;
}
const products: Product[] = getProductsSomehow();
const expensive = products.filter(product => product.price > 100);
The callback parameter product is correctly inferred only because products is typed.
Example: validating configuration
interface FeatureFlag {
key: string;
enabled: boolean;
}
const flags: FeatureFlag[] = loadFlags();
const betaFlag = flags.find(flag => flag.key === 'beta-dashboard');
Again, the callback parameter type depends on the array type.
Real Codebase Usage
In real codebases, developers usually solve this by typing data close to where it enters the system.
Common patterns
1. Define interfaces or types for domain models
export interface User {
id: number;
name: string;
aliases: string[];
occupation: string;
gender: string;
height: {
ft: number;
in: number;
};
hair: string;
eyes: string;
powers: string[];
}
This creates a reusable contract for the rest of the app.
2. Type imported data immediately
import rawUsers from '../data.json';
import { User } from './types';
const users: User[] = rawUsers;
This is better than letting untyped data spread through the application.
3. Use guard clauses for missing results
const user = users.find(user => user.id === query);
if (!user) {
return res.status(404).send({ message: 'No User found with the given id.' });
}
return res.status(200).send({ message: 'Success', user });
This is a common Express pattern because .find() returns undefined when nothing matches.
4. Validate route parameters
const query = Number(req.params.id);
if (Number.isNaN(query)) {
return res.status(400).send({ message: 'Invalid id.' });
}
This prevents bad input from reaching the search logic.
5. Prefer typed imports over require where possible
Using import usually works better with TypeScript's type system than using untyped require(...).
Common Mistakes
1. Typing only the callback parameter instead of the source data
This works:
Users.find((user: User) => user.id === query);
But if Users itself is untyped, you still have a weakly typed source. A better approach is:
const users: User[] = rawUsers;
users.find(user => user.id === query);
2. Using require() without clear types
Broken or weakly typed example:
const Users = require('../data');
const user = Users.find(user => user.id === 1);
Better:
import rawUsers from '../data.json';
const users: User[] = rawUsers;
3. Forgetting that .find() can return undefined
Broken example:
const user = users.find(user => user.id === query);
console.log(user.name);
If no user is found, this will fail.
Better:
const user = users.find(user => user.id === query);
if (user) {
console.log(user.name);
}
4. Using parseInt() carelessly
This is valid, but can be improved:
const query = parseInt(req.params.id);
Better:
const query = Number(req.params.id);
if (Number.isNaN(query)) {
return res.status(400).send({ message: 'Invalid id.' });
}
5. Assuming JSON automatically gives perfect TypeScript types
JSON is just data. TypeScript needs a type definition to understand its shape safely.
6. Confusing any with a real fix
You could silence the error like this:
const Users: any = require('../data');
But this turns off type safety. It hides the problem instead of solving it.
Comparisons
| Approach | Example | Pros | Cons |
|---|---|---|---|
Untyped require | const Users = require('../data') | Quick to write | Weak inference, more any, less safety |
| Typed import + interface | import rawUsers from '../data.json'; const users: User[] = rawUsers; | Best type safety, clear data shape | Requires interface definition |
| Type callback parameter only | users.find((user: User) => ...) | Fixes the immediate callback error | Does not fully type the source data |
Use any | const Users: any = ... | Removes compiler complaints fast | Loses most TypeScript benefits |
any vs typed data
| Option | Meaning |
|---|---|
any | TypeScript stops checking that value properly |
User | One object with the expected user fields |
User[] | An array of user objects |
| `User | undefined` |
Cheat Sheet
TS7006 quick fix checklist
TS7006means TypeScript cannot determine a parameter type safely.- Callback parameters often get their type from the array or function they belong to.
- If
.find(user => ...)fails, check the type of the array first. - Define an interface for your data shape.
- Type imported JSON as
YourType[]. - Remember:
.find()returnsT | undefined.
Useful syntax
interface User {
id: number;
name: string;
}
const users: User[] = rawUsers;
const user = users.find(user => user.id === query);
const user = users.find((user: User) => user.id === query);
Safer Express example
const query = Number(req.params.id);
if (Number.isNaN(query)) {
return res.status(400).send({ message: 'Invalid id.' });
}
const user = users.find(user => user.id === query);
if (!user) {
return res.status(404).send({ message: 'No User found with the given id.' });
}
return res.status(200).send({ message: 'Success', user });
Rule of thumb
If a callback parameter becomes any, type the input data source first.
FAQ
Why does TypeScript say a parameter implicitly has any type?
Because TypeScript cannot infer the parameter type, and your settings disallow unnamed any values.
Why is the .find() callback parameter not inferred correctly?
Because the array being searched is not clearly typed, so TypeScript has no reliable item type to use for the callback parameter.
Should I type the callback parameter manually?
You can, but it is usually better to type the array or imported data source so inference works everywhere.
How do I type JSON data in TypeScript?
Create an interface for the JSON shape, import the JSON, and assign it to a variable typed as that interface or array of interfaces.
What type does .find() return in TypeScript?
It returns the matching item type or undefined if no match is found.
Is using any okay to fix TS7006?
It removes the error, but it also removes type safety. It is usually a poor long-term solution.
Is require() worse than import in TypeScript?
Not always, but import usually works more smoothly with TypeScript's type inference and module typing.
How should I type my Express route data?
Define a domain type such as User, type the source data as User[], validate request input, and handle the case where no item is found.
Mini Project
Description
Build a small Express-style user lookup module that loads typed user data and returns one user by ID. This project demonstrates how proper typing fixes TS7006, improves autocomplete, and makes .find() callbacks safe and predictable.
Goal
Create a typed user lookup flow where JSON-like data is modeled with interfaces and searched safely by ID.
Requirements
- Define TypeScript interfaces for the user data structure.
- Store a small array of users using the typed interface.
- Write a function that accepts an ID and returns the matching user.
- Handle the case where the ID is invalid or no user is found.
- Use
.find()without triggeringTS7006.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.