Skip to content

prevent [object Object] in placeholder slots #2697

@paperclover

Description

@paperclover

Description

It is easy to make the mistake of formatting an object in a template placeholder, instead of a property on it.

<div>
  <p>good morning ${input.user}</p>
  //                      ^^^^
  // oh no it's supposed to be "user.name", build succeeds
</div>

I haven't configured TypeScript with Marko (especially within neovim) but i would guess that this doesn't error there since that syntax in a template string passes type checking (which is true since all objects can coerce to strings, it just isn't useful in this context)

I propose that the Marko language tools make this a type-checking error, and additionally a runtime error:

Unexpected object in template placeholder: '{ name: "clover" }'. To emit a literal '[object Object]', use ${String(value)}

Possible Implementation & Open Questions

One stretch of this proposal is to also ban null and undefined. While I have not actively run into this issue, I feel like it might be possible to hit it and accidentally emit a blank slot instead of properly handling the null case. It is a familiar mechanic of React/JSX, but the case of { condition ? <div /> : null} is already solved more beautifully with <if=condition><div/></>. I'm curious about real world use cases of null in template content, I think I'm missing something extremely obvious that prevents this from being a reasonable addition.

As for just plain objects being stringified, I have this implemented in the clover sitegen tool as an overlay to the Marko HTML runtime, since my adoption of the language is still light. So this is only part of the way there.

export function escapeXML(input: unknown) {
  // The rationale of this check is that the default toString method
  // creating `[object Object]` is universally useless to any end user.
  if (
    (typeof input === "object" && input &&
      // only block this if it's the default `toString`
      input.toString === Object.prototype.toString)
  ) {
    throw new Error(
      `Unexpected object in template placeholder: '` +
        util.inspect({ name: "clover" }) + "'. " +
        `To emit a literal '[object Object]', use \${String(value)}`,
    );
  }
  return marko.escapeXML(input);
}

Is this something you're interested in working on?

Yes

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions