tc39 / proposal-string-cooked

ECMAScript proposal for String.cooked built-in template tag
MIT License
52 stars 5 forks source link

Function signature bikeshed #6

Open jamiebuilds opened 3 years ago

jamiebuilds commented 3 years ago

Splitting this conversation off from #1 since I think it might generate a fair bit of conversation.

I'm going to use TypeScript syntax in other to describe the function signatures, along with the TemplateStringsArray type defined in TypeScript:

interface TemplateStringsArray extends ReadonlyArray<string> {
  readonly raw: readonly string[];
}

I'm going to make a couple assumptions from the motivation of this proposal:

Tag signature

declare function cooked(strings: TemplateStringsArray, ...values: any[]): string;

Two-arrays signature

declare function cooked(strings: TemplateStringsArray, values: any[]): string;

Other signatures

Feel free to suggest other signatures, please include any pros / cons.

ljharb commented 3 years ago

I think being able to use it as a direct tag is quite useful. Imagine an API like:

function doSomething(tag = String.cooked) {
  return tag`special string ${}`;
}

obviously all of that is super contrived, but hopefully it illustrates the utility of having an "identity" tag function that can be used as a default value when no other tag is provided.

bathos commented 3 years ago

While I personally have only had the need for this in the delegation cases, the syntactic tag case is actually what specifically led me to write something up: seeing someone advising folks to alias String.raw as html for tagging HTML strings to take advantage of tooling (highlighters, Prettier processing) that handles tagged-templates-where-the-tag-is-named-html specially (i.e., not lit-html or something but just regular strings.)

I'd encountered this practice once before and it seemed like if multiple people have stumbled on this "solution" - both times not aware of its consequences for escape sequences - then it's a bit of a footgun as-is for raw to stand up there all alone. It's understandable why folks don't realize what raw does precisely because there is no counterpart to contrast it with: "oh, I found the default tag!"

I wouldn't expect a feature to be designed around concerns like "I want to give my editor a signal about how to highlight this", but I think those examples hint at particular dev intuitions which would be better served by a "real" tag.

jamiebuilds commented 3 years ago

Okay, I was under wrong impression then. I'll update the issue

tabatkins commented 3 years ago

Strongly on the "tag signature" bandwagon, for the reasons already given. In particular, if the name cooked is chosen, the obvious and strong parallel with raw would make it very confusing if it had a different signature.

zloirock commented 3 years ago

I'd prefer the two-arrays signature. The "identity" use case makes no sense in most cases - it's better to use just a literal, for the "cooked" use case, arrays should be changed, so makes no sense to accept a tag - it's an additional load on the call stack and the spread operator can be just forgotten.

jamiebuilds commented 3 years ago

it's an additional load on the call stack and the spread operator can be just forgotten.

I don't think it is particularly meaningful overhead that it should be specifically designed to prevent anyone from ever using it that way. And I don't think we should make things different for the sake of making them different.

devingfx commented 2 months ago

+1 for tag signature... This is already a standard for all the things that is meant to be used around template litterals ! Even though an utility function is not meant to be a tag, it is usefull sometimes to directly pass arguments around:

const myTag = ( ...args )=> {
    const sanitized = utils.sanitizeTemplateLiteral( ...args )
    let result = String.cooked( ...sanitized )
    result = result.replace( /foo/g, 'bar' )
    ...
}

Knowing that this form of arguments signature ([ string[], ...any[] ]) is related to template litterals makes a kind of pattern to look/test/parse ...

const myTagOrNotTag = ( ...args )=> {
    const isTpl = Array.isArray( args[0] ) && args.length == args[0].length
    if( isTpl )
    ...
}
zeel01 commented 2 months ago

Not having the signature match String.raw would be incredibly confusing. Half the point of this proposal seems to stem from the fact that String.raw is misunderstood in and of itself. Ideally, with this proposal implemented, anyone that was using String.raw incorrectly can just do a quick find and replace with String.cooked and now their code works how they thought it did. The other signature idea seems to come from a dislike of the template tag signature to begin with - a dislike I can agree with! But since that's what we've got, we might as well stick with it instead of making a new method that's really really similar but not quite the same.