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?
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>
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;
Most helpful comment
Any updates on this? Plain
fails in the online playground.