Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incomplete support for tagged template literals: String.raw doesn't work #2616

Closed
Macil opened this issue Oct 11, 2016 · 10 comments
Closed

Incomplete support for tagged template literals: String.raw doesn't work #2616

Macil opened this issue Oct 11, 2016 · 10 comments
Labels
bug ES2015+ Has PR Library definitions Issues or pull requests about core library definitions

Comments

@Macil
Copy link
Contributor

Macil commented Oct 11, 2016

It seems like Flow's type definition for String.raw is broken because String.raw appears to always fail:

/* @flow */
const s = String.raw `abc\ndef ${'aaa'} \n`;
console.log(s);
$ flow version
Flow, a static type checker for JavaScript, version 0.33.0
$ flow
test.js:2
  2: const s = String.raw `abc\ndef ${'aaa'} \n`;
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ encaps tag. Function cannot be called on any member of intersection type
  2: const s = String.raw `abc\ndef ${'aaa'} \n`;
               ^^^^^^^^^^ intersection
  Member 1:
  274:     static raw(templateString: string): string;
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function type. See lib: ../flow/flowlib_2457d6cd/core.js:274
  Error:
    2: const s = String.raw `abc\ndef ${'aaa'} \n`;
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ array. This type is incompatible with
  274:     static raw(templateString: string): string;
                                      ^^^^^^ string. See lib: ../flow/flowlib_2457d6cd/core.js:274
  Member 2:
  275:     static raw(callSite: $Shape<{raw: string}>, ...substitutions: any[]): string;
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function type. See lib: ../flow/flowlib_2457d6cd/core.js:275
  Error:
  275:     static raw(callSite: $Shape<{raw: string}>, ...substitutions: any[]): string;
                                       ^^^^^^^^^^^^^ object type. Expected object instead of. See lib: ../flow/flowlib_2457d6cd/core.js:275
    2: const s = String.raw `abc\ndef ${'aaa'} \n`;
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ array


Found 1 error

Here's Flow's type definition for String.raw:

    static raw(templateString: string): string;
    static raw(callSite: $Shape<{raw: string}>, ...substitutions: any[]): string;

The first overload just seems wrong because String.raw can't be called on plain strings. The second overload isn't perfect (substitutions ought to be string[] I think. And what is $Shape for? Would {raw: string} not be enough?) but I think it looks like it ought to work. Maybe Flow doesn't treat tagged template strings as having a raw property?

@Macil Macil changed the title Incorrect type definition for String.raw Incorrect type definition for String.raw / Incomplete support for tagged template literals Oct 11, 2016
@Macil Macil changed the title Incorrect type definition for String.raw / Incomplete support for tagged template literals Incomplete support for tagged template literals: String.raw doesn't work Oct 11, 2016
@mroch mroch added bug ES2015+ Library definitions Issues or pull requests about core library definitions labels Oct 11, 2016
@DimitryDushkin
Copy link

So, what is correct type for template strings?

@kumar303
Copy link

kumar303 commented Mar 25, 2017

As the message says, I guess it takes an array? I was able to pass flow-check for this code...

const someString = dedent`A long template string...`;

...with this declaration of dedent:

declare function dedent(params: Array<*>): string;

(I don't know what to put as the Array parameters though)

@Macil
Copy link
Contributor Author

Macil commented Mar 26, 2017

The type of a tagged template function should look like this in general (possibly with a different array type for the value parameter):

declare function exampleTemplateFunction(templateParts: string[] & {raw: string[]}, ...values: any[]): any;

(I'm not really sure if Flow correctly understands this though.)

I think String.raw's declaration ought to look like this:

declare function stringRaw(templateParts: string[] & {raw: string[]}, ...values: Array<string|number>): string;

but I think Flow might still report an error when String.raw is used with a template string if it doesn't understand the types passed to tagged template functions correctly.

@jeffijoe
Copy link

Any updates on this? Plain

String.raw`template`

fails in the online playground.

@bradennapier
Copy link

bradennapier commented Nov 20, 2017

+1

Yeah would be nice to support this.

You can essentially get coverage on the fn but then anyone that calls it gets an error or you can get errors in the fn and the calls to it will work kind of. I've been trying various combinations for the below template literal fn

const TRAILING_COMMENTS = /\s+#.*$/gm;
const SURROUNDING_WHITESPACE = /^\s+|\s+$/gm;
const LITERAL_NEW_LINES = /[\r\n]/g;

export default function re(flags: string) {
  return (strings: { raw: Array<*> } & Array<*>, ...values: Array<?RegExp>) => {
    function toPattern(pattern: string, rawString: string, i: number) {
      let value = values[i];

      if (value == null) {
        return pattern + rawString;
      }

      if (value instanceof RegExp) {
        value = value.source;
      }

      return pattern + rawString + value;
    }

    const compiledPattern = strings.raw
      .reduce(toPattern, '')
      .replace(TRAILING_COMMENTS, '')
      .replace(SURROUNDING_WHITESPACE, '')
      .replace(LITERAL_NEW_LINES, '');

    return new RegExp(compiledPattern, flags);
  };
}

export const RE_LUA_COMMENTS = re('g')`
  (?:
    --\[\[(([^\s]+)\s+=>[\S\s]*?)\]\]
  )|
  (?:
    --\s*\|\s*([^\s\n]+):\s+([^\n]+)
  )
`;

@jml6m
Copy link

jml6m commented Jan 23, 2019

+1

@leebyron
Copy link
Contributor

f8875fd is part of the fix, however https://github.com/facebook/flow/blob/master/lib/core.js#L329-L330 seems wrong.

As a standard libdef it should probably be:

static raw(template: string[] | $Shape<{raw: string}>, ...substitutions: Array<string | number>): string;

However that implies that you could call it as a normal function and supply an array of strings which you cannot do. Maybe flow could add a special case for this function, @mroch ?

@Macil
Copy link
Contributor Author

Macil commented Jan 25, 2019

@leebyron I think my post above has the right types, and shouldn't need any special-casing from Flow: #2616 (comment). & should be used instead of | to signify it must be both an array and an object with a raw property, and $Shape should not be used because the raw property is not optional. Including number in the substitutions type seems fine. I'm going to edit my linked post to match that because that seems to be more consistent with how Flow allows strings and numbers to be combined freely.

@talbenari1
Copy link
Contributor

Just ran into this as well. If we're going to be precise about this, looking at the spec, the correct typedef for the first parameter would be something along the lines of { raw: { length: number, [index: number]: string } } except with immutable properties, but there's a snowball's chance in hell that Flow will be able to support something structurally typed like that.

@goodmind
Copy link
Contributor

This isn't error in library definitions @jbrown215 correct me if I'm wrong but first argument of template tag should flow into upper bound of $ReadOnlyArray (aka <T: $ReadOnlyArray<string>>, but currently it is just strictly Array<string>

flow/src/typing/statement.ml

Lines 3220 to 3226 in aaa8067

let reason_array = replace_reason_const RArray reason in
let ret = Tvar.mk cx reason in
(* tag`a${b}c${d}` -> tag(['a', 'c'], b, d) *)
let call_t =
let args =
let quasi_t = DefT (reason_array, bogus_trust (), ArrT (ArrayAT (StrT.why reason, None))) in

So this should work like this

declare class TemplateStringsArray extends $ReadOnlyArray<string> {
  +raw: string;
}

// error
declare function raw(strings: {|+raw: string|}, ...substitutions: any[]): string;
// error
declare function raw(strings: number, ...substitutions: any[]): string;
// no error
declare function raw(strings: $ReadOnlyArray<string>, ...substitutions: any[]): string;
// no error
declare function raw(strings: TemplateStringsArray, ...substitutions: any[]): string;
// no error
declare function raw(strings: Array<string>, ...substitutions: any[]): string;

facebook-github-bot pushed a commit that referenced this issue Feb 1, 2023
Summary:
Tagged template literals produce an array with a read-only `raw` property that contains an array of strings. The current type is simply wrong, and [we didn't correctly type `String.raw` at all](https://flow.org/try/#0MYewdgzgLgBAHjAvDAylATgSzAcwHToCGA7gAYBmIIpA3EA).

Notes:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw
- https://tc39.es/ecma262/#sec-gettemplateobject
- https://tc39.es/ecma262/#sec-string.raw

Changelog: [errors] Fix type created by tagged template literals and `String.raw`

Closes #7580
Fixes #5705
Fixes #2616

Reviewed By: SamChou19815

Differential Revision:
D42731497 (a6fa1c2)

------------------------------------------------------------------------
(from 3af4b54520666520018907478c76470a3adf171a)

fbshipit-source-id: 625089fc1e1c3accd6e9c201822d2e45e36bd6d7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug ES2015+ Has PR Library definitions Issues or pull requests about core library definitions
Projects
None yet
Development

No branches or pull requests

10 participants