151

I am using following code to get unique numbers:

let uniques = [ ...new Set([1, 2, 3, 1, 1]) ]; // [1, 2, 3]

However, typescript report following error: Type 'Set' is not an array type. I am not typescript ninja, could someone tell me what is wrong here?

2
  • 4
    I think that's just a Typescript bug, if the version you're using claims to support ES2015.
    – Pointy
    Commented Nov 1, 2015 at 16:55
  • 1
    @Pointy Sorry about that, I should include version of tsc which is 1.6.2
    – Eggy
    Commented Nov 1, 2015 at 17:19

7 Answers 7

170

Update: With Typescript 2.3, you can now add "downlevelIteration": true to your tsconfig, and this will work while targeting ES5.

The downside of downlevelIteration is that TS will have to inject quite a bit of boilerplate when transpiling. The single line from the question transpiles with 21 lines of added boilerplate: (as of Typescript 2.6.1)

var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spread = (this && this.__spread) || function () {
    for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
    return ar;
};
var uniques = __spread(new Set([1, 2, 3, 1, 1]));
console.log(uniques);

This boilerplate will be injected once per file that uses downlevel iteration, and this boilerplate can be reduced using the "importHelpers" option via the tsconfig. (See this blogpost on downlevel iteration and importHelpers)

Alternatively, if ES5 support doesn't matter for you, you can always just target "es6" in the first place, in which case the original code works without needing the "downlevelIteration" flag.


Original answer:

This seems to be a typescript ES6 transpilation quirk . The ... operator should work on anything that has an iterator property, (Accessed by obj[Symbol.iterator]) and Sets have that property.

To work around this, you can use Array.from to convert the set to an array first: ...Array.from(new Set([1, 2, 3, 1, 1])).

8
  • @Restam : Does typescript provide polyfills for Array.from in IE if "target":"es5" in tsconfig.json ?
    – Rohit Rane
    Commented Jul 28, 2017 at 10:00
  • 1
    @jackOfAll No, Typescript doesn't do any polyfilling of prototypes for you. If you set "target": "es5" it should give you a compiler error if you attempt to use a method that needs to be polyfilled.
    – Retsam
    Commented Jul 29, 2017 at 14:52
  • 4
    @Restam great solution with Array.from. Most other people seem to just give up on this. thanks for a real solution!
    – rayepps
    Commented Aug 14, 2017 at 17:59
  • It's not a bug, they just don't support it for the es5 target (see github.com/Microsoft/TypeScript/issues/4031). Array.from should work if you have es2015 or higher (es2017, esnext) in your lib list in tsconfig. Commented Nov 27, 2017 at 1:29
  • 1
    @SimonHänisch Thanks for the link: I've updated my answer, I no longer call it a "bug", but a "transpilation quirk", which is probably a more accurate term. I also added information about the downlevel iteration option from that link, which also solves the original issue.
    – Retsam
    Commented Nov 27, 2017 at 16:33
131

You can also use Array.from method to convert the Set to Array

let uniques = Array.from(new Set([1, 2, 3, 1, 1])) ;
console.log(uniques);

4
  • What's the point of spreading the array only to recapture it in a new array? Commented Feb 13, 2018 at 4:41
  • 1
    If not possible to target "es6", in tsconfig. And using Set with spread operator is required, how would you do it?
    – Nate Getch
    Commented Feb 14, 2018 at 17:58
  • 4
    The point is that if you use Array.from(), you no longer need the spread operator. It just adds overhead. let uniques = Array.from(new Set([1, 2, 3, 1, 1])); Commented Feb 15, 2018 at 8:14
  • 2
    @RobbyCornelissen the whole reason for above code is to make an array with unique values of original array
    – Reza
    Commented Aug 18, 2021 at 1:46
61

This is a missing feature. TypeScript only supports iterables on Arrays at the moment.

1
  • Thanks for clarification. I will use .filter() or something else to get job done. I also found few issues on github about this particular error. I will keep an eye on this in future releases.
    – Eggy
    Commented Nov 2, 2015 at 11:26
44

In Javascript:

[ ...new Set([1, 2, 3, 1, 1]) ]

In Typescript:

Array.from(new Set([1, 2, 3, 1, 1]))

In React State (setState):

setCart(Array.from(new Set([...cart, {title: 'Sample', price: 20}])));
21

You need to set "target": "es6", in your tsconfig.

0
12

Now, you can use Set in your Typescript setup (No need to target es6):

In you tsconfig.json, add this line:

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    
    "downlevelIteration": true,                  /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    
  },
  ...
}
4

To make it work, you either need "target": "ES6" (or higher) or "downlevelIteration": true in the compilerOptions of your tsconfig.json . This resolved my issue and working good or me.Hope it will help you also.

Not the answer you're looking for? Browse other questions tagged or ask your own question.