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 higherThis 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 likeSetrequires ES2015+ features - TypeScript's default target might be set to older versions (like
es5) for compatibility - The
--downlevelIterationflag enables this functionality for older targets but adds runtime overhead
Best Practices
- Prefer modern targets: Use at least
es2015for new projects - Use Array.from() for compatibility: When supporting older environments
- Consider performance:
Setoperations 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.