Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
TypeScript Magic (artsy.github.io)
65 points by skilled on March 12, 2023 | hide | past | favorite | 21 comments


Relying on hacks for production code is usually a red flag/code smell that indicates there was probably a better alternative. `string & {}` definitely feels like one of those hacks.

As an alternative, wouldn't it be better to map the list of supported values in a structure like an enum?

Example:

```

enum HelloWorldValue {

  Hello: 'hello',

  World: 'world'
}

const value = HelloWorldValue.Hello

```

And now you can call that enum when declaring a variable value and get its contents in autocomplete. As a nice bonus, because these are references and not strings, you can ask your IDE for all places instantiating those values, which can be a life saver.


This is what happens when you are building rules are around an existing system versus the other way around.

In another language you have a value of type String which can take any value, might crash, might not, when you give it a color value.

And then you have a set of items like you described, which are convertible to String (or are a string, like your enum).

In fact, the receiver property probably shouldn't even be String, but typeof Color, which can be constructed through a method/constructor that ensures the String input is a valid format.

Because which color is "XXXXXX"?


Based on the last time I played with thml colours, probably red


It's also a lot of effort for a language which seems to be incapable of runtime type checking. Typescript is a glorified linter


> This is what happens when you are building rules are around an existing system versus the other way around.

exactly right!


This is a neat trick especially if your brain got rewired to think about union and disjoint types. I recently had to do something like this to force separatuon of required arams that go into a media control wrapper:

   type ControlParams = { type: 'audio' } & ControlParamsAudio | { type: 'video } & ControlParamsVideo

   const ControlsWRapper : React.FC<ControlParams> = <>....</>

Boom, you have proper autocomplete and typechecks for audio controls or for video controls depending on your type.


Similar trick for JSON-based web API's that returns either error message or result

    async fetchApi<TResult = void>(request: Request): Promise<TResult> {
      type SuccessfulApiResponse = { Succeeded: true; Result: TResult }
      type FailedApiResponse = { Succeeded: false; Error: string | null | undefined }
      type ApiResponse = SuccessfulApiResponse | FailedApiResponse;
      const response = await fetch(request);
      ensureStatusOK(response);
      const result = await response.json() as ApiResponse
      if (!result.Succeeded) throw new Error(result.Error || 'Unknown API error');
      return result.Result
    }


This seems purely like a hack to get autocomplete to help you out. But it does nothing to the actual type. It’s still just a string, any string.

Not that I mind this at all. Autocomplete is a first class concern for all my typescript work.


In our codebase this will never be allowed:

type ColorHexValue = `#${string}` type ColorRGBValue = `rgb(${number},${number},${number})`

At least one developer will ask to find an alternative -- use actual CSS names or use a function to create these strings, treat them as strings and forget all those funky typing, becausenobody knows what color a combination of HSV values looks like and nobody would be able to maintain that code. Just because it is possible doesn't mean people should use it.


Agreed.

I like CSS, but the article (while interesting and informative) give me PTSD thinking about a previous project with "typed" CSS. Maybe I'm an old fart, but I never saw the appeal in having such a thorough level of typing.

The downside is longer time to analyse your Typescript code, having to reference some ungodly type if you need to pull your value from a config file, while the upside is just... something that most IDE already did anyway?

In most case, I'm pretty sure it's less of an hassle to say it's a damn string and check that your code give some correct value there, instead of trying to rube-goldberg Typscript into auto-generating the correct code.

Typescript is powerful, but KISS is good too.


I don't understand what you meant by "nobody knows what color a combination of HSV values" (but you quote HEX strings)? What makes the quoted code hard to maintain?


That’s a cool insight about TypeScript’s type handling! Since it’s just about one specific case, the title of the blog post should ideally be a bit more specific, in my opinion.


Agreed! I thought the same.


I'm by no means, a typescript expert - but this seems like over-engineering to me, types are nice, but you don't need perfect typing, in fact it can drastically slow you down.

There needs to be a good medium, I'll add as much to my code so my ide can decorate things whether it's php @var docs, or typescript, but typing every string would drive me mad, now -- if it comes from the DB, that's a whole different story.


Wow I was struggling with this exact problem earlier today. I scraped a list of all css color names into a union so I could get them in my autocomplete, but also wanted to allow any valid css color without having to make template literals for every possible valid color string.

Stealing this!! Thanks for sharing!


awesome! author here. I was at the same place and was looking with others and we found this in a corner of the internet. now it's on two corners of the internet, hopefully it will be easier for others to find.

you have made my day, knowing it helped at least one person!


This is a really neat trick! I might even use it in production one day.

But it's still unintuitive enough that it should only be used in production with a reference to this article.


I don't quite get the point. If the function takes an argument of "hello", "world" or string, doesn't it still just take any string?


The aim here is less about the types (like you say, any string will be valid) and more about autocomplete. When you start a string literal, you can technically type anything in there, but certain answers are more "correct" than others - i.e. these theme colour names. This technique allows you to tell Typescript-as-LSP which names it should suggest, while also letting Typescript-as-typechecker know that other options are also allowed.


Isn't this a branded type / newtype / opaque type / nominal type?

Edit: Ah I see, this only affects tooltips/autocomplete, doesn't actually change the behaviour of the type.

If you want to do that you need to do something like:

  type UserId = number & { __type: 'UserId' };


This is both a really useful trick but also, I think, way too pally with the exact implementation. It feels like Microsoft could “fix” this at any moment. I mean, they probably won’t…




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: