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

Created on 12 Oct 2016  路  10Comments  路  Source: facebook/flow

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?

ES2015+ Has PR Library definitions bug

Most helpful comment

Any updates on this? Plain

String.raw`template`

fails in the online playground.

All 10 comments

So, what is correct type for template strings?

As the message says, I guess it takes an array? I was able to pass flow-check for this code...
js 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)

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.

Any updates on this? Plain

String.raw`template`

fails in the online playground.

+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]+)
  )
`;

+1

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 ?

@leebyron I think my post above has the right types, and shouldn't need any special-casing from Flow: https://github.com/facebook/flow/issues/2616#issuecomment-289257544. & 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.

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.

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>

https://github.com/facebook/flow/blob/aaa8067e425e4d362f3e1aa886bea85cd46bc5d6/src/typing/statement.ml#L3220-L3226

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;
Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  路  3Comments

pelotom picture pelotom  路  3Comments

Beingbook picture Beingbook  路  3Comments

mjj2000 picture mjj2000  路  3Comments

jamiebuilds picture jamiebuilds  路  3Comments