top | item 41269150

(no title)

kipple | 1 year ago

> The actionable tip here is to start with the data. Try to reduce code complexity through stricter types on your interfaces or databases. Spend extra time thinking through the data structures ahead of time.

This is why I love TS over JS. At first it feels like more work up front, more hurdles to jump through. But over time it changed how I approached code: define the data (& their types) first, then write the logic. Type Driven Development!

Coming into TS from JS, it might feel like an unnecessary burden. But years into the codebase, it's so nice to have clear structures being passed around, instead of mystery objects mutated with random props through long processing chains.

Once the mindset changes, to seeing data definition as a new first step, the pains of getting-started friction are replaced by the joys of easy future additions and refactors.

discuss

order

umvi|1 year ago

(tangential) In theory I like TS. But in practice, unless I'm the one writing it and can KISS, it can quickly turn into an unmaintainable nightmare that nobody understands. TS astronauts, left unchecked, can make extremely complex types using generics, conditionals, and all the esoteric features of TS, resulting in extremely obtuse code.

For example, I doubt anyone could explain this "type" without studying it for several hours:

https://github.com/openapi-ts/openapi-typescript/blob/main/p...

In this case, the "type" is really an entire program.

steve_adams_86|1 year ago

I must be a part of the problem because reading that type isn't too difficult.

I also think types like this aren't innately problematic when they live in libraries. They should be highly focused and constrained, and they should also be tried, tested, and verified to not get in the way, but they can absolutely be a huge benefit to what we do.

Maybe it's mostly problematic when type astronauts litter the application layer with types which are awful abstractions of business logic, because types are far less intuitive as programs than regular JavaScript or common data structures can be. Just type those in the dumbest way possible rather than wrap the definition of them up into monolithic, unnavigable, nested types and interfaces.

If a library allows me to define valid states related to events which drive a data store or something narrow like this, that's awesome (assuming it's intuitive and out of the way). I like this kind of type-fu. If it's trying to force coworkers to adhere to business logic in unintuitive ways, in a domain that's not unlikely to shift under our feet, that's a huge problem.

spoiler|1 year ago

> TS astronauts, left unchecked, can make extremely complex types using generics, conditionals, and all the esoteric features of TS, resulting in extremely obtuse code.

Disclaimer: I guess I'm a fellow TS astronaut.

Most of the time TS astronauts will stick to your methodology of keeping things simple. Everyone likes simple, I think.

However, the type-austronautics is necessary once you need to type some JS with some really strange calling conventions/contracts (think complex config objects inputs, or heterogenous outputs that end up with _not quite the same_ call signatures, using large objects trees for control flow, etc; basically really shit code that arises from JS being a very dynamic language) without modifying the interfaces themselves. Sure you can be a bit lenient, but that makes the code unsound and crates a false sense of security until the inevitable runtime bug happens.

The correct solution would be to refactor the code, but that's not always possible. Especially if your manager was the author of said magnum anus—apologies, I meant magnum opus—and sabotages any attempts at refactoring.

I guess the moral hiding in this anecdote is that I should looking for a new job.

koito17|1 year ago

I will agree that some TS libraries have insanely complicated types, and compared to other programming languages I have used (e.g. Clojure), it takes a longer time to understand library code.

But the example provided here doesn't seem too bad. Here is my attempt after skimming it twice.

  Paths extends Record<string, Record<HttpMethod, {}>>
I assume the

  Record<HttpMethod, {}>
is a hacky (but valid) way to have a map where the keys must be HttpMethod and the values contain arbitrary map-like data. e.g. maybe it describes path parameters or some other specific data for a route.

Moving on.

  Method extends HttpMethod
  Media extends MediaType
These seem self-explanatory. Moving on.

  <Path extends PathsWithMethod<Paths, Method>, Init extends MaybeOptionalInit<Paths[Path], Method>>(
    url: Path,
    ...init: InitParam<Init>
  ) => Promise<FetchResponse<Paths[Path][Method], Init, Media>>
Looks like we have two generic parameters: Path should be a type satisfying PathsWithMethod<Paths, Method>. That's probably just requiring a choice of path and associated HTTP method. As for Init, that looks like it's to extract certain route-specific data, probably for passing options or some payload to fetch.

Lastly,

  Promise<FetchResponse<Paths[Path][Method], Init, Media>>
Taken everything I have just guessed, this represents an async HTTP response after fetching a known valid path -- with a known valid method for that path -- together with Init parameters passed to fetch and possibly Media uploaded as multi-part data.

I probably got some details wrong, but this is what I surmised in about 15 seconds of reading the type definition.

RangerScience|1 year ago

Not defending that code - and I agree with you that wild TS code gets nightmarish (I usually call it a “type explosion”) but

Waaaay back when in my C++ days, starting to get into template metaprogramming, the “aha!” moment that made it all much easier was that the type definition could be thought of as a function, with types as input parameters and types as output parameters

Recentlyish, this same perspective really helped with some TS typing problems I ran into (around something like middleware wrapping Axios calls).

It’s definitely a “sharp knife” if you overuse it, you screw yourself, but when you use it carefully and in the right places it’s a super power.

fshbbdssbbgdd|1 year ago

Back when I used to work in plain js I saw very complicated structures, but there is no type annotation. It’s worse. The horrible part is where the type changes in different cases, so you have to trace everything to know if what you are changing is safe.

bko|1 year ago

You don't have to understand it necessarily to use it. I'm sure there's plenty of library level code that people don't understand. But that's the point. Typescript will tell you if you screw up. A lot of this has to do with generics, and if you're using it and typescript can infer the generic types to use, it'll be a lot simpler and you'll know exactly what's breaking.

And for libraries like this, you'll unfortunately be limited to Typescript ninjas to maintain, but there's no alternative really. I guess use javascript without types, which doesn't remove the dependencies or complexity just hides it away, and who knows what happens at run time

appplication|1 year ago

Oh this is so funny. That exact type was my introduction to typescript! I came over from Python a few months ago for a solo web project, and I struggled with that type mightily!

In the end it took me a few tries to land on something idiomatic and I actually just ended up using inferred types (which I think would be the recommended way to use it?) after several iterations where I tried to manually wrap/type the client class that type supports. Before I found a functional pattern for inferring the type correctly, I was really questioning the wisdom of using typescript when probably several whole days were spent just trying to understand how to properly type this. But in doing so I learned essentially everything I currently know about TS (which admittedly is still very limited) so I don’t think it was wasted time.

distrill|1 year ago

Yes, I find myself in type hell with some regularity. TBH it happens with my own codebase too when libraries I want to use are authored by these type astronauts.

BurningFrog|1 year ago

I worked in fully typed Java for 8 years before jumping to fully untyped Ruby.

Having leaned hard on the Java type system for many years, I was terrified of the type anarchy.

But it turned out to not be a problem at al. For me at least, being ambitious with writing tests made not miss types at all. In practice, a good test suite catches pretty much any problems typing handles, and then some!

This is only my experience. I'm not saying everyone should or could work that way, or that I'm better than you etc.

HideousKojima|1 year ago

>For example, I doubt anyone could explain this "type" without studying it for several hours

From skimming it for about a minute it seems like it's just a strongly typed way to generate HTTP requests? It really doesn't look too complicated

acchow|1 year ago

For your linked example…

It has documentation

> This type helper makes the 2nd function param required if params/requestBody are required; otherwise, optional

The type here is the implementation not the documentation. I guess we are so used to types being the documentation, which they are for value/function level programming, but not in type level programming.

I think maybe you are disappointed at the tooling? I do think the docs here should be attached to the type so that it appears in the IDE as well.

herpdyderp|1 year ago

Honestly that type you linked looks like a dream to use (I've never OpenAPI). I love APIs that enforce any necessary restrictions on the type level.

xyst|1 year ago

side note: don’t have much experience with TS, but the overuse of extend is also common in “enterprise” Java/C# apps

_heimdall|1 year ago

Type-driven development has been a big win for me as well, specifically when writing web front ends. Whether its client side rendering (sometimes a necessary evil) or on the server with a tool like Astro, I try really hard to start by defining types and UI components totally separately.

I'll actually build out the full data flow and UI components in complete isolation, leaving the glue code for the final step. Its kind of a weird pattern from what I've seen, I have gotten some interesting code reviews over the years, but it really is nice focusing on one concern at a time. At the end its also fun watching a bit of glue code wire up the entire app.

robertlagrant|1 year ago

I think this is definitely the best way. It feels like you're violating DRY, but you're not.

RangerScience|1 year ago

Agree overall (yay data structures, and that types prompt thinking about them) but I don’t think you need types to make good data structures, and, just because you have types doesn’t mean you end up with good data structures.

But yes, definitely - working in a typed language encourages that mindset, and it’s the application of that mindset that yields the benefits (imo).

throwanem|1 year ago

To a decent first approximation, and especially given TypeScript's erasure and generally very opt-in design approach, types are just tests the compiler doesn't allow you to not run.

That's not a good way to think about them forever. But it might be a good way to start thinking about them, for those as yet unfamiliar or who've only had bad experiences.

(I've had bad early experiences with a lot of good tools, too, when learning to use them fluently required broadening my perspective and unlearning some leaky prior intuitions. TypeScript was one such tool. I don't say that's the only reason someone would bounce, but if that's the reason you-the-reader did so, you should consider giving it a more leisurely and open-minded try. You may find it rewards your interest more generously than you expected it might.)

bunderbunder|1 year ago

When you've really got your data structures and the rules for manipulating them pinned down, and you've built a good interface on top of it, the result is usually something that's so simple and easy to understand that it kind of doesn't matter anymore whether you're working in a static or dynamically typed language.

IOW, I think that the value in static typing (speaking only about this specific issue!) isn't that it makes you do things well; it's that it puts a limit on how poorly you can do them. But I also sometimes worry that it puts a limit on how well people can do, too. I've met way too many people who tacitly believe that all statically typed domain modeling is automatically good domain modeling.

0xbadcafebee|1 year ago

Strict types are a great way to paint yourself into a corner. Good design should only impose strict types within a single module, with very loose coupling outside the module (meaning loose types)

Having a well defined data model is important, but you often can't really know what that data model should be until you've banged on a prototype. So the faster (in the long run), "better" way is to first prototype with very loose types and find what works, and then lock it down, within the scope of the above paragraph

wtetzner|1 year ago

> Strict types are a great way to paint yourself into a corner.

I've never really understood this stance. It's all code. It's not like you can't change it later.

> So the faster (in the long run), "better" way is to first prototype with very loose types and find what works, and then lock it down, within the scope of the above paragraph

I think this depends on the programmer, and what they're experienced with, how they like to think, etc. For example, as counterintuitive as it might seem, I find prototyping in Rust to be much quicker than in Python.

forrestthewoods|1 year ago

> So the faster (in the long run), "better" way is to first prototype with very loose types and find what works, and then lock it down, within the scope of the above paragraph

Disagree. You can still prototype and refactor with strict types. I don't find working with loose types to be faster at all. Once a program reaches non-trivial complexity loose types make iteration development significantly more difficult and error prone.

JamesSwift|1 year ago

Why loosen the types on the API, when the type system is fully capable of encoding higher-level restrictions? That then locks you into that API as a contract. If you instead lock the API down to the strict set, you are free to expand it over time.

tonetegeatinst|1 year ago

Iv started with learning python and java during highschool but python really stuck.

Now as I work on my degree iv started to try to learn C for reverse engineering and low level development. While I do understand some things its a big leap in terms of skill in python. I love how flexible it is and how fast it is. Recently I started a new challenge based on shodfycast's most recent video. ( https://www.youtube.com/watch?v=M8C8dHQE2Ro ) and currently am just focusing on single thread performance, and using array structures. Then I realized my random number isn't true random and debating if my prg is sufficient enough. I also debated generating all the numbers at once into an array, throwing it into CPU cache, then doing the logic for rolls using the faster memory so I'm not waiting for numbers to generate. Single core laptop time is like 56 minutes using wsl.

I'm tempted to try this on my dual socket system using tinycore Linux, so I can shave off some time from useless overhead and use some debug tools to find the slow spots.

Unsure how much time I should sink into this though.

constantcrying|1 year ago

>Unsure how much time I should sink into this though.

You should stop immediately once you start learning less and are fixating on hyper specific problems.

>I also debated generating all the numbers at once into an array, throwing it into CPU cache

You don't control the cache. I recommend that you treat the CPU as a black box, strictly until you can no longer so so. If you are learning C and aren't even writing multi threaded code, you should not fixate on the specifics of how the CPU handles memory.

Please pretend that is true. Manipulating cache is difficult. And you should worry about other things. This will become important when you are doing multi threading.

brightball|1 year ago

The funny thing is that...this is why I like dynamic languages. Because I do all of this with the database and when I'm using something like Rails, ActiveRecord handles all of the types automatically based on what I've already defined in the database.

For web apps at least, about 90% of the data coming into and out of the application is just being stored or retrieved from the database. Converted from string to datatype in the DB and then converted back to string as it's being returned back. Enforcing types strictly in this layer that is largely pass-through can create so much additional boilerplate with minimum benefit since it's already handled where it matters.

For a NoSQL DB, sure, I get it. You NEED the application to define your types because your database isn't.

And then there are people who feel very strongly about having it everywhere, all the time and can't imagine working without it.

I like that we work in a field where we can have options.

rewgs|1 year ago

I totally agree, though this process isn't without its faults. One thing I've had to learn to be mindfull of is "type-crastination" -- if I'm not careful, I can really overdo it and spend way too much time and energy on defining types.

constantcrying|1 year ago

You can think about data structures in a fully dynamic language like JS or python. But you have to write software in a way, which utilizes these types and acknowledges that they exist.

Thinking about what your data structures are is important in any language. Strict typing helps you in pinning them down and communicating them, but the approach is not exclusive to strict typing.

Once your software is about passing anything objects around, you have already lost and proper thinking about data structures becomes impossible. I agree that stricter typing helps you to avoid that trap.

shreddit|1 year ago

That’s why I really like to write some c++ in my free time after a week of javascript at work

superfist|1 year ago

From js to c++?! Come on... For sure there is at least one thing js and c++ have in common - crappy standard library

hamandcheese|1 year ago

I feel similarly about FP. It can be more work up front, but what a gift it is to your future self when f(x) == f(x).