Typescript: Support spread operator ('...') on strings

Created on 2 Apr 2015  ยท  17Comments  ยท  Source: microsoft/TypeScript

  • [ ] In function calls
  • [ ] In array literals

For each of these, we may have to consider surrogate pairs.

ES6 Spec

Most helpful comment

This still seems like a bug since we don't conform with ES6.

All 17 comments

Currently we do not support spread on strings. The main reason is we do not want ot do type system directed emit.

two questions:

  • Should we add a helper function that extracts the array from a string in cases we do not know the type?
  • Should we handel string literals?

@mhegazy What is the spec issue here?

this is a question more than an issue. we do not support spread on strings. should we?

This still seems like a bug since we don't conform with ES6.

Of note - if you are going to consider surrogate pairs here, there's a number of other places where it may be significant which might require polyfill for ES3/ES5. For example,

const guitar = '\u{1F3B8}';
for (let symbol of guitar) {
  console.log(symbol);  //expect one call here (TypeScript 1.5 ES5 target makes one call per surrogate pair)
}

Considering that, it might be best to restrict this to ES6+ target.

Was this implemented in 2.0?

Spread on strings is quite important, but I guess some work need to be done to get the sample below to work in non-ES6 environments.

let str = 'x\uD83D\uDE80y';

    // ES5: \uD83D\uDE80 are (incorrectly) reversed
    console.log(str.split('').reverse().join(''));
        // 'y\uDE80\uD83Dx'

    // ES6: order of \uD83D\uDE80 is preserved
    console.log([...str].reverse().join(''));
        // 'y\uD83D\uDE80x'

Just so people understand what we're up against, it's surrogate pairs. Taking the example of "a๐Ÿ€œ" (a "normal" character followed by a surrogate pair), it has some fairly odd behavior (shown here in Chrome console):
image

Specifically notice that:

  • The length is 3 but there are only 2 glyphs
  • Spreading into an array produces 2 elements
  • Array#slice produces 3 elements

In Dojo, we have a for ... of shim which provides iteration over strings that meets the ES6 spec. Obviously emitting that for strings is rather complicated to be able to spread them. I don't know if that helps at all, but I guess something like this could be adapted into a helper function.

@RyanCavanaugh There's no problem with this... This is a validated spec of ES6. The spread operator is viable on any iterable object. The string is one.
Moreover, the [..."a๐Ÿ€œ"] // ["a", "๐Ÿ€œ"] is totally valid and should not be compared to the length of the string or the Array.prototype.splice method.

@ChibiBlasphem Ryan is not suggesting that [ ...'a๐Ÿ€œ' ] is invalid, it is just not easily down-emittable.

When your target is es2015 or beyond, there is no issue with this and TypeScript allows it. When your target is es5 or es3, there is no easy down emit that can easily spread a string, because what is available natively, does not deal with surrogate pairs. The only way to deal with it is to emit some sort of helper function that detects the surrogate pairs and splits up the characters properly.

You can check how es6 is doing it.
They can transpile it to es5 version. For es3 I don't really know if it's possible.

You can check how es6 is doing it.

ES6 is using iterators. It isn't doing it in user space. As I linked to, there are ways to do it, but the down emit is rather complex. It would look something like this (which is fine for ES5 and ES3), which handles the pairs correctly, but this would be another helper for a rather limited situation:

function __spreadString(str) {
  var l = str.length;
  var result = [];
  for (let i = 0; i < l; ++i) {
    var char = str[i];
    if ((i + 1) < l) {
      var code = char.charCodeAt(0);
      if ((code >= 0xD800) && (code <= 0xDBFF)) {
        char += str[++i];
      }
    }
    result.push(char);
  }
  return result;
}

console.log(__spreadString('a๐Ÿ€œ')); // ["a", "๐Ÿ€œ"]
console.log(__spreadString('x\uD83D\uDE80y').reverse()); // ["y", "๐Ÿš€", "x"]

Bump. So why isn't this supported yet? I was quite surprised that the spread operator works on strings in ES6 but not TS.

@JakeStoeffler the comments above make that clear... supporting surrogate pairs in ES5 properly is complicated to ensure the behaviour is consistent with the spec. It would a) require a helper function and b) have a typed based emit which TypeScript is fundamentally opposed to.

This issue title confused me for a second.

This is about downlevel emit, not about supporting it at the type level. If --downlevelIterators, or --target is es2015 or newer it will behave correctly.

Is it so complicated to take Babel implementation which can handle this properly? ๐Ÿ˜ญ

Would be nice to at least add a warning that ES5/ES3 target can't emit this correctly. It's quite an old issue at this point.

@rbuckton any chance your work on array spread might lead to looking into this at some point?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

weswigham picture weswigham  ยท  3Comments

MartynasZilinskas picture MartynasZilinskas  ยท  3Comments

Roam-Cooper picture Roam-Cooper  ยท  3Comments

DanielRosenwasser picture DanielRosenwasser  ยท  3Comments

kyasbal-1994 picture kyasbal-1994  ยท  3Comments