Fixing "Type 'Set<unknown>' can only be iterated through when using the '--downlevelIteration' flag"
Problem
When working with TypeScript and trying to use the spread operator with Set
objects to create arrays of unique values:
const uniqueValues = [...new Set(someArray)];
You may encounter this TypeScript error:
Type 'Set<unknown>' can only be iterated through when using the '--downlevelIteration' flag
or with a '--target' of 'es2015' or higher
This occurs because the spread operator syntax for iterating over Set
objects requires JavaScript features that weren't available in older ECMAScript versions.
Solutions
1. Update TypeScript Target (Recommended)
The most straightforward solution is to update your tsconfig.json
file to use a modern ECMAScript target:
{
"compilerOptions": {
"target": "es2015", // or "es6", "es2017", "es2020", etc.
// other options...
}
}
TIP
The es2015
target includes native support for Sets and the spread operator, eliminating the need for the --downlevelIteration
flag.
2. Use Array.from() Instead
If you can't change your TypeScript target, use Array.from()
to convert the Set to an array:
// Instead of: [...new Set(values)]
const uniqueValues = Array.from(new Set(values));
This approach works with older ECMAScript targets and doesn't require the spread operator.
3. Alternative: Filter Method
For compatibility with very old JavaScript environments, you can use the filter()
method:
const uniqueValues = values.filter((value, index, array) => {
return array.indexOf(value) === index;
});
While this approach works, it's less efficient for large arrays compared to using a Set.
4. Complete Configuration Example
For React projects with TypeScript, here's a recommended configuration:
{
"compilerOptions": {
"target": "es2015",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}
React Note
If you update from es5
to a newer target in a React project, you might also need to update React.Node
to React.ReactNode
in your type annotations.
Understanding the Issue
The error occurs because:
- The spread operator (
...
) on iterable objects likeSet
requires ES2015+ features - TypeScript's default target might be set to older versions (like
es5
) for compatibility - The
--downlevelIteration
flag enables this functionality for older targets but adds runtime overhead
Best Practices
- Prefer modern targets: Use at least
es2015
for new projects - Use Array.from() for compatibility: When supporting older environments
- Consider performance:
Set
operations are generally faster than array filtering for deduplication
Example Implementation
Here's the correct way to get unique IDs from an array of objects:
// Original problematic code
const uniqueMuscle = workoutexercices.map((exercice: any) => {
let exercicesId = exercice.id;
exercicesId = [...new Set(exercicesId)]; // Error here
return exercicesId;
});
// Fixed version
const uniqueMuscleIds = Array.from(
new Set(workoutexercices.map(exercice => exercice.id))
);
This approach extracts all IDs first, then creates a Set to remove duplicates, and finally converts back to an array - all in a single efficient operation.
Choose the solution that best fits your project's compatibility requirements and performance needs. For most modern applications, updating the TypeScript target to es2015
or higher is the recommended approach.