Building Fluent Interfaces in TypeScript

Поделиться
HTML-код
  • Опубликовано: 17 окт 2024
  • TypeScript has many great tools for building great fluent interfaces! In this video, we take a look at some of the best ways to do that.
    Accompanying blog post: shaky.sh/fluen...
    Mastodon: social.lol/@shaky

Комментарии • 84

  • @fagnersales532
    @fagnersales532 2 месяца назад +9

    That's a topic I really love, it makes programming with TypeScript feel magic. Unfortunately, couldn't understand really how to implement that. I'll for sure read the blog post, and I'd love to have more content on this.

    • @andrew-burgess
      @andrew-burgess  2 месяца назад +1

      Thanks for the feedback! Is there anything specific that was confusing or that I didn't explain well?

  • @zeeeeeman
    @zeeeeeman 2 месяца назад +5

    👏 holy crap that piping explanation was great .Thank you. Subbed!

  • @temoncher
    @temoncher 2 месяца назад +1

    Nice explanation! Probably TypeScript is the best language for fluent interfaces because of its flexible type system

  • @RyanDsouza-be2qx
    @RyanDsouza-be2qx 2 месяца назад

    Thanks for this vid! I always forget that functions can also have properties on them and this makes for interesting patterns like this :)

  • @LazyDeveloper-1
    @LazyDeveloper-1 2 месяца назад

    Great video! I love when great typing improves the overall developer experience a lot! Keep on going!

  • @akam9919
    @akam9919 11 дней назад

    I wish I saw this waaaaaaaay earlier. BTW, for the table function in the last example, you can actually use it with just "return this as QueryBuilder".
    This however will not work for versions less than 5.1.6. Library authors seeking to support older TS versions or projects stuck on such codebases will have to create a new object unfortunately.

  • @mickdavies5647
    @mickdavies5647 2 месяца назад

    Great job! Where was this video when I was starting to get my mind around how Promises 'really' work? 😊

  • @ghostinplainsight4803
    @ghostinplainsight4803 2 месяца назад +1

    @Andrew Burgess can you make a video on types for currying and composition if it's possible.

  • @vicentelyrio6332
    @vicentelyrio6332 26 дней назад

    your channel is gold

  • @minyoungna6642
    @minyoungna6642 2 месяца назад

    This inadvertently solidified my understanding of effect.to haha,because you “run” an effect after you defined them, and they are not executed at the time they are written.

  • @azizsafudin
    @azizsafudin 2 месяца назад

    That table method to update the type is also used in trpc’s meta(), and Zod too I believe, common pattern used in libraries

  • @Shuyinz
    @Shuyinz 2 месяца назад +1

    This is absolutely fantastic!

  • @shashydass4114
    @shashydass4114 2 месяца назад

    Great video and blog post. Succinct and very informative!

  • @rockNbrain
    @rockNbrain 2 месяца назад +3

    Great job dude !

  • @furycorp
    @furycorp 2 месяца назад

    Doesn't this risk hurting tree shaking e.g. zod vs. valibot -- i.e. if not done carefully it can blow up client-side bundles? Either way nice video thanks for sharing your knowledge :) These interfaces do have their place!

  • @athulgeorge6744
    @athulgeorge6744 2 месяца назад +1

    With the query builder example how would you make it show a TS error when you call more than 1 select method. And how could you limit the callable methods depending on the stage of the query. For example the only option should be the select method when you type "q." . But after you do that, then the only option should be the from() method, when you type "q.select()." ? Would be cool to see how this is done, similar to how drizzle have done it.

    • @xDivisionByZerox
      @xDivisionByZerox 2 месяца назад +3

      You would split your logic into multiple containers. You could have build the initial QueryBuilder class with only a "select" method. This select method would then return an instance of another class, lets call it SelectState. SelectState itself has only a single method as well, "from" which returns another container (FromState for example). you continue that logic and only implement the methods that you want to appear on the containers query. Through all that you propably have to pass along the previous containers or generic types from those containers. I can try to setup a minimal example and come back later.

    • @athulgeorge6744
      @athulgeorge6744 2 месяца назад

      @@xDivisionByZerox Yeh this makes sense. I assumed that how it would be built, and yes I would love to see a minimal example of this.

    • @a-minimum-of-three-characters
      @a-minimum-of-three-characters 2 дня назад

      It doesn't necessarily need to be a separate class. Another way you could go about it is by down casting your return value with 'Pick'ing the functions you want for the next chain link. You could even use the 'extends' keyword to inspect the input to return branched chains.

  • @Caldaron
    @Caldaron 2 месяца назад +5

    isnt this pipe chaining super hard on linting performance?

    • @SXsoft99
      @SXsoft99 2 месяца назад +2

      just remove the linter
      or
      just wait a bit longer, as long as the code gets cleaner and more human readable who cares about 1 sec extra wait time

  • @ayushporwal5950
    @ayushporwal5950 2 месяца назад

    I will revisit the video again after reading blog, it was a bouncer after the pipe function xD

  • @trentcox9239
    @trentcox9239 2 месяца назад

    your damn right i want you to go through how your building that query builder with that typescript black magic! easy sub.

  • @KaiDeLorenzo
    @KaiDeLorenzo 2 месяца назад +2

    In Rust it's pretty straightforward to create different states within the builder object so you can for example prevent (at compile time) calling select more than once in a single query (if you haven't created a subquery). How might one do that with typescript?

    • @mk72v2oq
      @mk72v2oq 2 месяца назад +2

      It is possible in TS, but would be quite messy and complicated.

    • @athulgeorge6744
      @athulgeorge6744 2 месяца назад +1

      @@mk72v2oq Im pretty sure u get TS errors when you do this with drizzle

    • @toonv4023
      @toonv4023 2 месяца назад

      You make use of a state generic where the output is a transformed version of the input, but usually following the same specs (T extends {..}, U extends {..same}). By saving the literal value of what you change into the output generic, you can hide/show properties or methods in the chain. It's not that hard and writing it once makes you understand a lot more type definitions.
      Your returned 'this' can most easily achieve this using a helper type ToggleMethods, for example.

    • @mk72v2oq
      @mk72v2oq 2 месяца назад

      I think the easiest way is to represent every state as a separate type.
      Yes, you can construct types on the fly with Pick/Omit etc. But that makes consumer's life miserable in case they want to pass the object around and need to specify the type.

    • @KaiDeLorenzo
      @KaiDeLorenzo 2 месяца назад

      ok my comments keep getting deleted because i tried to include links :( . anyway i was linking to "The Typestate Pattern in Rust" by Cliff L. Biffle. You can search for it. What I'm realizing now is that the biggest thing that's possible in Rust but not Typescript is the ability to specify to the compiler that an object has gone out of scope or been used up or transitions to a new state.
      In Typescript you can correctly transform into the next "type state" which only has the correct methods to call. However the object that you transformed is still a valid object that you can transform again. This is fine for builder type patterns where the methods aren't actually doing anything until executing the "finalize" method. However, in scenarios where the method calls are actually doing work/side affects and the "type states" represent real world state being able to "revert" to a previous state can result in bugs.
      Does anyone have any thoughts how you might prevent this regression to a previous state via compiler errors in Typescript?

  • @veritatas678
    @veritatas678 2 месяца назад

    i recently tried to do sth similar for my own trpc and this would have helped

  • @coolbeatguy
    @coolbeatguy 2 месяца назад

    Nice tutorial 👍

  • @aghileslounis
    @aghileslounis 2 месяца назад

    Excellent video

  • @AlexGrand
    @AlexGrand 2 месяца назад +2

    The main difference between OOP and FP for "fluent interfaces" is that you should understand that in OOP most of the time you "mutate" the instance, so there's no easy way to "clone" it. In FP, each .pipe will create a new function, so it's easy to split the same chain to 2 or more cases.
    The main example why "mutating" is bad is preparing `list` endpoint where you are doing COUNT() in one query, and limit/offset in another. I FP way you just add base query and splitting it to two variants: count and data. With OOP you should implement own "cloning" or create 2 queries with the same conditions

  • @bangunny
    @bangunny 2 месяца назад +2

    my brain is not braining right now with all the A, B and C

    • @13odman
      @13odman 2 месяца назад

      Definitely a mind bender

    • @trejohnson7677
      @trejohnson7677 Месяц назад

      It's literally algebra. Literally.

  • @Danielo515
    @Danielo515 2 месяца назад

    I was a bit discouraged by the first simple example, but I had faith, and watch until he very end

  • @aro_matt
    @aro_matt 2 месяца назад

    The pipe brings kotlin's apply to my mind.

  • @cossth
    @cossth 2 месяца назад +8

    It is similar to builder pattern.

    • @Caldaron
      @Caldaron 2 месяца назад +17

      it IS the builder pattern!

    • @parlor3115
      @parlor3115 2 месяца назад +1

      ​​@@CaldaronIt's the upgrade form of the pattern. With each call, your options narrow down accordingly to what's permitted.

    • @CuriousSpy
      @CuriousSpy 2 месяца назад

      @@parlor3115 it is a builder pattern

  • @thedelanyo
    @thedelanyo 2 месяца назад

    I think this might be the basis of, Drizzle orm

  • @diegoulloao
    @diegoulloao 2 месяца назад

    Nice video

  • @hugodsa89
    @hugodsa89 2 месяца назад +4

    So a builder pattern?

  • @SoreBrain
    @SoreBrain 2 месяца назад

    I would love to receive ur Blog posts as rss

    • @andrew-burgess
      @andrew-burgess  2 месяца назад +1

      Thanks! You totally can! shaky.sh/follow/

    • @SoreBrain
      @SoreBrain 2 месяца назад

      @@andrew-burgess thank you! Looking forward to more posts. Btw this was a great video, a perspective/topic I haven't seen on yt yet.

  • @Gruak7
    @Gruak7 2 месяца назад

    Rust is king in this pattern

  • @vim55k
    @vim55k 2 месяца назад +1

    Called builder pattern

  • @ivands16
    @ivands16 2 месяца назад +1

    Never do this if you care about load performance and bundle size. Because you can't treeshake any of the methods on a class. Just create simple standalone functions. As reference look at the benefits of `valibot` over `zod`

    • @astropajamaland
      @astropajamaland 2 месяца назад +1

      Interesting point. I’m curious if there’s a way to have the best of both worlds.
      Perhaps a Babel-like build tool that can detect which Zod methods you actually use, and replace Zod with a stripped down version containing only those methods at runtime. I wonder if anything like this exists.

    • @13odman
      @13odman 2 месяца назад

      Don’t throw the baby out with the bath water. It all depends on the size of the library.

  • @OmgImAlexis
    @OmgImAlexis 2 месяца назад +1

    isn’t this just the builder pattern..?

    • @Boroujerdi
      @Boroujerdi 2 месяца назад

      Yes, but fluent. Also called fluent builder pattern

  • @r-i-ch
    @r-i-ch 2 месяца назад

    Man, does this make me feel stupid. With all the generics I am very confused. Alas. Off to go read the blog post. Maybe that will make me feel less dumb!

    • @andrew-burgess
      @andrew-burgess  2 месяца назад +1

      Oof, not what I want! If you have any feedback on how I could have explained this better, I'm always open to it! Thanks for watching & reading!💚

  • @AlexanderYaremchuk
    @AlexanderYaremchuk 2 месяца назад

    pipe is a monad!

  • @hyperprotagonist
    @hyperprotagonist 2 месяца назад +1

    Keep the facial hair. It suits you ❤

  • @mumk
    @mumk 2 месяца назад

    interesting

  • @Omeha2
    @Omeha2 2 месяца назад

    Good luck with debugging such things....

  • @cossth
    @cossth 2 месяца назад

    FC

  • @ThomazMartinez
    @ThomazMartinez 2 месяца назад

    f classes

  • @safarl45
    @safarl45 2 месяца назад

    So dumb lol why not just write sql in the database with stored procedures???

  • @fozzy-x86
    @fozzy-x86 2 месяца назад

    How about an implementation of QueryBuilder that has a strongly typed build() function? tinyurl querybuilder-stringly-typed

    • @andrew-burgess
      @andrew-burgess  2 месяца назад

      oh, man, that's so cool, I've never thought of that! love it, thanks for sharing!