Question
In TypeScript, why does this code produce the error Argument of type 'string' is not assignable to parameter of type 'never'?
const foo = (foo: string) => {
const result = []
result.push(foo)
}
I expected result to accept a string because foo is a string. What is TypeScript inferring here, why does this happen, and how should this code be written correctly? Is this a TypeScript bug or intended behavior?
Short Answer
By the end of this page, you will understand why TypeScript sometimes infers an empty array as never[], why that causes .push() to fail, and how to fix it by giving the array an explicit type or enough context for inference. You will also see how this fits into TypeScript's type inference rules in real projects.
Concept
TypeScript tries to infer types automatically from your code. When it sees this:
const result = []
there is no information about what kind of values the array should contain. In some contexts, TypeScript infers the type as never[].
never is a special TypeScript type that means this can never happen or no value is possible here. So a never[] means:
- this is an array
- but it can never contain any value
That is why this fails:
result.push(foo)
If result is inferred as never[], then push() expects values of type never. A string is not assignable to never, so TypeScript reports an error.
This is intended behavior, not a bug. TypeScript is being cautious because the empty array alone does not provide enough information about the element type.
To fix it, you need to tell TypeScript what the array should contain:
Mental Model
Think of TypeScript as a very careful organizer labeling boxes.
If you write:
const result = []
you have given it an empty box with no label.
TypeScript asks:
- Should this box hold strings?
- numbers?
- objects?
- booleans?
Since it cannot safely guess, it may label the box as:
- a box that should never receive anything
That is what never[] means.
Then when you try to put a string into the box:
result.push(foo)
TypeScript says: "You told me this box accepts nothing, so I cannot allow a string either."
The solution is to label the box up front:
const result: string[] = []
Now TypeScript knows this box is for strings.
Syntax and Examples
Core idea
When creating an empty array in TypeScript, the element type may need to be provided explicitly.
Problem example
const foo = (foo: string) => {
const result = []
result.push(foo)
}
TypeScript may infer:
const result: never[]
So this becomes invalid:
result.push(foo) // string is not assignable to never
Correct ways to write it
1. Explicit array type
const foo = (foo: string) => {
const result: string[] = []
result.push(foo)
}
This is the clearest option. It tells TypeScript that result is an array of strings.
2. Type assertion
Step by Step Execution
Consider this code:
const addName = (name: string) => {
const names = []
names.push(name)
}
Here is what happens step by step:
-
TypeScript reads the function parameter:
name: stringSo
nameis known to be a string. -
TypeScript reads the empty array:
const names = []There are no elements, so there is no direct clue about the array's element type.
-
In this situation, TypeScript may infer:
const names: never[] -
TypeScript then checks this line:
names.push(name) -
If
namesis , then only accepts values.
Real World Use Cases
This issue commonly appears in real code whenever you start with an empty array and fill it later.
1. Building a filtered result
const getShortNames = (names: string[]) => {
const result: string[] = []
for (const name of names) {
if (name.length < 5) {
result.push(name)
}
}
return result
}
2. Collecting validation errors
const validateUser = (name: string, age: number) => {
const errors: string[] = []
if (!name) errors.push("Name is required")
if (age < 18) errors.push("Must be at least 18")
return errors
}
3. Transforming API data
Real Codebase Usage
In real projects, developers usually avoid this problem in a few common ways.
Explicitly type accumulators
This is very common in loops, reducers, and helper functions.
const ids: number[] = []
const messages: string[] = []
Use typed function return values
A return type can make the intent clearer.
const collectTags = (input: string[]): string[] => {
const tags: string[] = []
for (const item of input) {
tags.push(item.trim())
}
return tags
}
Use array methods that preserve type information
Methods like map and filter often avoid manual empty-array setup.
const names = users.map(user => user.name)
Common Mistakes
1. Assuming TypeScript will always infer the array element type
Broken example:
const result = []
result.push("hello")
Why it fails:
- The empty array gives no clear element type.
- TypeScript may infer
never[].
Fix:
const result: string[] = []
result.push("hello")
2. Using any[] just to silence the error
const result: any[] = []
result.push("hello")
result.push(123)
result.push(true)
Why this is risky:
- It removes useful type safety.
- Bugs become easier to introduce.
Better:
const result: [] = []
Comparisons
never[] vs other array typings
| Type | Meaning | Can you push a string? | Typical use |
|---|---|---|---|
never[] | Array that should contain no possible values | No | Usually appears from inference when TypeScript has no safe element type |
string[] | Array of strings | Yes | Names, messages, labels |
number[] | Array of numbers | No | Scores, IDs, counts |
unknown[] | Array of unknown values | Yes, but reading values requires narrowing | Generic external data |
Cheat Sheet
Quick fix
If you see:
Argument of type 'X' is not assignable to parameter of type 'never'
and the code includes an empty array like this:
const result = []
try typing the array explicitly:
const result: string[] = []
Key idea
- Empty arrays may not provide enough type information.
- TypeScript may infer
never[]. never[]accepts no values.
Common fixes
const result: string[] = []
const values: number[] = []
const items: MyType[] = []
Alternative fix
FAQ
Why does TypeScript infer never[] for an empty array?
Because an empty array alone does not tell TypeScript what element type it should contain. In some contexts, the safest inference is never[].
Is not assignable to parameter of type never a TypeScript bug?
No. It is intended behavior based on TypeScript's type inference rules.
How do I fix parameter of type never when using .push()?
Give the array an explicit type, such as:
const result: string[] = []
Should I use as string[] or : string[]?
Usually : string[] is better because it is clearer and more explicit.
What does never mean in TypeScript?
never means no value can exist for that type. It is often used for impossible code paths or failed inference scenarios.
Why does this happen more often with empty arrays than non-empty arrays?
A non-empty array gives TypeScript sample values to inspect. For example, clearly suggests , but gives no clue.
Mini Project
Description
Build a small TypeScript utility that collects valid usernames from an input array. This demonstrates how to correctly type an initially empty array and safely push values into it after validation.
Goal
Create a function that returns a string[] of valid usernames without triggering never[] errors.
Requirements
- Write a function that accepts an array of strings or undefined values.
- Create an empty result array with the correct TypeScript type.
- Skip undefined values and blank strings.
- Add trimmed valid usernames to the result.
- Return the final array of usernames.
Keep learning
Related questions
Angular formGroup Error Explained: Fixing 'Can't bind to formGroup' in Reactive Forms
Learn why Angular shows 'Can't bind to formGroup' and how to fix it by importing ReactiveFormsModule correctly.
Fix "Element implicitly has an 'any' type" in TypeScript Object Indexing
Learn why TypeScript rejects string object indexing and how to fix it with keyof, unions, and typed object keys in React.
Fix "Property has no initializer" in Angular TypeScript Components
Learn why Angular TypeScript shows "Property has no initializer" and how to fix it using defaults, optional properties, or definite assignment.