Giter VIP home page Giter VIP logo

Comments (10)

chrissantamaria avatar chrissantamaria commented on May 28, 2024 1

Opened #102 with all Option instances updated so we can move discussion there

from clack.

chrissantamaria avatar chrissantamaria commented on May 28, 2024

Thanks for looking at this so quickly! Looking at your PR answers my original question a bit - seems like the primitive type restriction is due to values being potentially stringified (and subsequently displayed) if there's no label:

const label = option.label ?? String(option.value);

On one hand I can get behind this, as non-primitives may not stringify cleanly. However, it does prevent valid use-cases of:

  • value type implementing a desired, human-readable toString method
  • label being provided, meaning value is never stringified -> shown anyways

The second case in particular feels like enough of a reason to remove the primitive restriction. In the label-less case, (imo) the judgement call should be up to consumers of this lib on whether or not the value.toString() implementation is sufficient. (afaik) String(option.value) will always return some sort of string, so there shouldn't be any runtime error concerns either.

That all being said, that's just my opinion! Totally understand if you'd like to keep things as-is - happy to maintain my own fork / patch in projects. Thanks again!

from clack.

ulken avatar ulken commented on May 28, 2024

value type implementing a desired, human-readable toString method

Why not explicitly provide that as label then?


IMO, more complex types could be handled in user land without much overhead. They ought to have a unique identifier to use as value.

Finding the parent object from the returned value is trivial.

from clack.

chrissantamaria avatar chrissantamaria commented on May 28, 2024

Why not explicitly provide that as label then?

That's fair - I'd be happy to provide my own label, though the types restrict me from providing any non-primitive value regardless. For example:

const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);

await select({
  message: '',
  options: [
    {
      label: `Today (${today.toLocaleDateString()})`,
      value: today, // type error
    },
    {
      label: `Tomorrow (${tomrorow.toLocaleDateString()})`,
      value: tomorrow, // type error
    }
  ],
});

Even though this behaves just fine at runtime and the date values are never stringified, the types prevent this type of use case. Stringifying the value in options + decoding the final result just to satisfy TypeScript feels like unnecessary overhead + complexity.

I could see this going either way:

  • remove value type restriction altogether, allowing for the above case + label-less cases (leaving control to consumers on whether value.toString() makes sense)
  • only allow non-primitive values when a label is also provided (this would likely require some TypeScript magic to support both cases)

from clack.

ulken avatar ulken commented on May 28, 2024

That's fair - I'd be happy to provide my own label, though the types restrict me from providing any non-primitive value regardless.

Right, I see your point.

Even though this behaves just fine at runtime and the date values are never stringified, the types prevent this type of use case. Stringifying the value in options + decoding the final result just to satisfy TypeScript feels like unnecessary overhead + complexity.

Like you've pointed out yourself it's not to please TypeScript per se, but rather the underlying implementation (i.e. allowing value as a fallback in a sensical way).

For the example at hand, doing a .toISOString() and then new Date() doesn't seem like much overhead, nor being complex to me. There's no performance issues to talk about either in such small data sets.

With that being said, with regards to your suggested solutions, I'm not in favor of # 1, but see no reason not to allow # 2, if technically possible (which I believe it is). I'll explore when I find the time.

from clack.

ulken avatar ulken commented on May 28, 2024

@chrissantamaria your example above is supported in the latest release.

from clack.

chrissantamaria avatar chrissantamaria commented on May 28, 2024

Really appreciate you taking a look at this! It does look like non-primitive types are allowed as values (🎉), but the return type of select is unfortunately unknown. Here's a full example (available on stackblitz as well):

import { select, isCancel } from '@clack/prompts';

const run = async () => {
  const today = new Date();
  const tomorrow = new Date(today);
  tomorrow.setDate(tomorrow.getDate() + 1);

  const value = await select({
    message: '',
    options: [
      {
        label: `Today (${today.toLocaleDateString()})`,
        value: today,
      },
      {
        label: `Tomorrow (${tomorrow.toLocaleDateString()})`,
        value: tomorrow,
      },
    ],
  });
  if (isCancel(value)) {
    return;
  }

  console.log({ value }); // value inferred as unknown
};

run().catch((e) => {
  console.error(e);
  process.exit(1);
});

Haven't had time to dig into the internals yet, though I'm happy to do so!

from clack.

ulken avatar ulken commented on May 28, 2024

Sorry for not getting back to you. That's a real bummer... why, oh why, TypeScript? Can't you be a little decent and help out, if only just a little?

I took a quick look at it but wasn't able to sort it out then. I'll try to get back to it ASAP. Maybe infer could help us out here?

Not sure what's worse, if we should revert this until properly fixed or not.

Thoughts @natemoo-re?

from clack.

chrissantamaria avatar chrissantamaria commented on May 28, 2024

I might be missing something, but just by reducing select to use a single Value generic (removing Options) it seems like the problem goes away: chrissantamaria@d174313

I imagine there's likely some reason this generic existed, though typechecking for the build still passes. If this feels like a reasonable solution I'm happy to put up a PR!

from clack.

ulken avatar ulken commented on May 28, 2024

Seems reasonable to me! Always good with fresh eyes...

What say you @natemoo-re?

Note: there are more components using Option which would need to be changed as well, but you're probably aware.

from clack.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.