How Did I Not Know This TypeScript Trick Earlier??!

Sdílet
Vložit
  • čas přidán 4. 07. 2023
  • This TypeScript trick to conditionally include types is so useful, especially for React props. I've asked myself how this is done many times before, and it's just really cool to learn something so simple yet useful.
    -- links
    Discord: / discord
    My GitHub: github.com/joschan21
  • Věda a technologie

Komentáře • 484

  • @mattpocockuk
    @mattpocockuk Před rokem +241

    Great video! Here's the correct terminology to help people google this stuff:
    Props is a discriminated union.
    'gender' is the discriminator.
    The if statements you use to check members of the union aren't type guards, that's something slightly different. You're doing 'narrowing'.
    Love seeing these tips out in the wild!

    • @vikingthedude
      @vikingthedude Před rokem +2

      I thought this was a parody account at first

    • @tif7305
      @tif7305 Před rokem +2

      Thank you for the terminology, I feel missing that makes it impossible to learn and compare techniques, so it is very much appreciated.

    • @gubatenkov
      @gubatenkov Před rokem +1

      so what is type guards then?

    • @tif7305
      @tif7305 Před rokem

      @@gubatenkov Type guard is when you test a variable on which type it is in an if statement to narrow it down. You can use e. g. instanceof, typeof or the in keyword for this. There is a great logrocket post about this, if you want to know more! just Google type guard typescript and you will find it

    • @mattpocockuk
      @mattpocockuk Před rokem +6

      @@gubatenkov Type guards are:
      const isString = (val: unknown): val is string => {
      return typeof val === 'string';
      }
      You use the 'is' syntax to annotate a function to be a 'type guard', or 'type predicate'.

  • @WebDevSimplified
    @WebDevSimplified Před rokem +419

    This is a trick I have been using for a while and really love it. One thing to note about this is that normal TS utility types like Omit and Pick will not work with Unions so you need to write your own version of these utils that look like this.
    export type UnionOmit = T extends unknown
    ? Omit
    : never

    • @satindar31
      @satindar31 Před rokem +4

      Thanks

    • @joshtriedcoding
      @joshtriedcoding  Před rokem +21

      Interesting. Thanks for sharing man!

    • @viktormalmedal265
      @viktormalmedal265 Před rokem +28

      Small tip, the type string | number | symbol is equal to the global type PropertyKey :)
      type Foo = PropertyKey;
      // ^? string | number | symbol

    • @nabinsaud4688
      @nabinsaud4688 Před rokem +1

      Why doesn't it create a table in my local db it pushes and migrate but does not create a table why tried many solutions not working

    • @amixengineer
      @amixengineer Před rokem +1

      Great trick, I used to get the same result with function overloading but that require a lot of type checking.

  • @vikingthedude
    @vikingthedude Před rokem +78

    For those curious, these are called tagged unions or discriminated unions. The typescript docs mentions them. They are common in functional languages and also in most newer languages like Rust. They are from a category of types called "sum types". What we're used to in OOP languages are usually "product types"

  • @chovnyk_pluve
    @chovnyk_pluve Před rokem +171

    I think that it's better to write code more explicit
    interface Person {
    name: string;
    }
    interface Male extends Person {
    gender: "male",
    salary: number;
    }
    interface Female extends Person {
    gender: "female";
    weight: number;
    }
    type Props = Male | Female;

    • @ensi.creator
      @ensi.creator Před rokem +11

      oh, that looks so much better

    • @lasindunuwanga5292
      @lasindunuwanga5292 Před rokem +4

      composition vs inhertitance?

    • @bernardcrnkovic3769
      @bernardcrnkovic3769 Před rokem +4

      @@lasindunuwanga5292 there is no inheritance because it is an interface. interfaces cant inherit as they dont have behavior

    • @lasindunuwanga5292
      @lasindunuwanga5292 Před rokem

      @@bernardcrnkovic3769 so you are telling me it is okay to build an heirachy of interfaces?

    • @rnz2363
      @rnz2363 Před rokem +33

      There is no need for using interfaces.
      Interfaces are much more extensive in terms of functionality than regular type aliases.
      Using the extends keyword is not more "explicit". Intersection types (with the & operator) have pretty much the same semantics.
      Interfaces should be used when describing abstract types (for example, when doing polymorphism).
      You should not use an interface if you're not planning on using the implements keyword on other concrete types.
      Interfaces are not as flexible as type aliases, because they only allow object types.
      Also, interfaces can be overwritten in other places of the code, just by having another definition of it, or even multiple other definitions.
      Type aliases are much more simple and generic, they are just a way to give some type a name in order to re-use it or export it.
      I would prefer something like that:
      type Person = {
      name: string
      }
      type Male = Person & {
      gender: 'male'
      salary: number
      }
      type Female = Person & {
      gender: 'male'
      weight: number
      }
      type Props = Male | Female

  • @bryson2662
    @bryson2662 Před rokem +361

    I believe these are called discriminated unions

    • @letfoobar
      @letfoobar Před rokem +12

      Yes, those are discriminated unions

    • @markzuckerbread1865
      @markzuckerbread1865 Před rokem +37

      I'm surprised so many people are finding out about this so late

    • @RonaldTetsuoMiura
      @RonaldTetsuoMiura Před rokem +155

      Males are required to declare their salary, while females are required to declare their weight? Indeed, a very discriminatory union 😂

    • @specy_
      @specy_ Před rokem +9

      @@markzuckerbread1865 i feel like disciminated unions are 90% of what makes typescript awesome, everyone should know it

    • @elfelipe1996
      @elfelipe1996 Před rokem +9

      It's so weird that the newer typescript docs removed the docs for discriminated unions. You can only find it in the old docs

  • @muhammedmaher6704
    @muhammedmaher6704 Před rokem +5

    I've been looking for this specific trick for a very long time to no avail! thank you man! 🔥

  • @martinharyanto8262
    @martinharyanto8262 Před rokem

    Please do more videos like this. This is very beautiful and and very nice. This can makes me refactor whole bunch of my component props type kudos Josh ! Great content !

  • @Ivcota
    @Ivcota Před rokem +8

    Something similar happens when using the Zod parser. If you look the generic output type, you’ll notice how the isSuccessful being true is constraint to the output that contains the data while isSuccussful being false is constraint to the output that produces and error. The parsing logic os handled within the library but can be a good source of inspiration for these types of unions.
    Great video 🎉

    • @joshtriedcoding
      @joshtriedcoding  Před rokem +1

      Mhmm that's interesting too. Thanks for sharing man, cheers

  • @jaredbecker3152
    @jaredbecker3152 Před 6 měsíci

    Saw this video when it first came out and thought it was really cool but never had a need for it until today so now I'm back to refresh my memory. Thanks Josh for this awesome little trick

  • @MisouSup
    @MisouSup Před rokem

    I just found your channel and I am really liking it. Good quality stuff :D

  • @Volbeast22
    @Volbeast22 Před 11 měsíci

    This was awesome man! Great explanation, been wondering how to get those conditional types to be so easy and you just nailed it!

  • @aminerhouma
    @aminerhouma Před 11 měsíci

    I really enjoyed learning that awesome trick you shared, especially with the helpful examples you provided. Your thoughts were also well-organized, which made it easy to follow along. Thank you!

  • @uixmat
    @uixmat Před 11 měsíci

    I was trying to figure out how to do this recently with no luck, so glad you uploaded this mate!

  • @user-bo3sq1wh4l
    @user-bo3sq1wh4l Před měsícem

    I spent the whole day today trying to figure out how to do this. Thanks to you and CZcams recommendations for this info!

  • @jenewland1999
    @jenewland1999 Před rokem +16

    Great vid Josh 👍🏻 Always amazing when you learn these sorts of tricks in TS. One I learnt yesterday that's pretty cool is if you want intellisense for a string type that has set responses as well as the ability to pass any string you can do: type Gender = "male" | "female" | "prefer not to say" | (string & {}); now when you bring up autocomplete rather than getting nothing you'll get male, female, and prefer not to say listed in the dropdown. (Credit to Matt Pocock for this one)

  • @poizonncs8264
    @poizonncs8264 Před rokem

    Love the way you explained, Loud & Sharp.
    Subscribed

  • @BlurryBit
    @BlurryBit Před rokem

    Life saving!!! I was looking for this for a long time. Thanks for the tip man! :)

  • @AsToNlele
    @AsToNlele Před rokem

    Loved the examples, thank you Josh!

  • @CrankyBarbar1an
    @CrankyBarbar1an Před rokem +8

    THIS IS HONESTLY SO COOL. I was actually in a similar position, where I wanted conditional typesafety, but I legit couldn't find exactly what I was looking for. And this video was just that. I honestly loved it, THANK YOU SO MUCH!!

    • @joshtriedcoding
      @joshtriedcoding  Před rokem

      You're welcome dude yeah I was really happy when I discovered this too

  • @psyferinc.3573
    @psyferinc.3573 Před rokem +1

    im glad your getting into typescript

  • @eof_lemongrab
    @eof_lemongrab Před rokem

    Keep up the great work Josh!

  • @AnindoSarker
    @AnindoSarker Před rokem

    I randomly found out your channel and I'm happy. Quality content as always

  • @daromacs
    @daromacs Před rokem

    amazing Josh, thanks for sharing and to the point, also noticing the real use cases!

  • @jakeave
    @jakeave Před 11 měsíci

    This is exactly what I've been looking for. I've kind of just dealt with the possibly undefined properties. Great explaination!

  • @maomorin
    @maomorin Před rokem

    DAAAAAAAMN!!! I have searched for this ages!!! I tried to make it with conditionals but this way is just what I needed :D thanks for share!

  • @jacob_dmn
    @jacob_dmn Před 10 měsíci

    Really needed this since a long time!!

  • @yatinm9250
    @yatinm9250 Před rokem

    Great video. I needed this quite recently. Thanks.

  • @HorizonHuntxr
    @HorizonHuntxr Před rokem +1

    This is awesome! I ran into rhis problem recently and ended up just allowing the user to pass all the props without discrimination but knowing this now is a huge help!
    The only downside i see to this is that you will keep getting typerrors until you fulfill all the requirements by passing all the necessary props but that's a small trade off for something so powerful

    • @joshtriedcoding
      @joshtriedcoding  Před rokem +1

      You could still make them optional! There really are some great benefits to this

  • @donaldsilveira
    @donaldsilveira Před rokem

    Thank you so much for this video, you just shipped it when I was looking how to do exactly it! 😂😂

  • @christopherreif3624
    @christopherreif3624 Před 11 měsíci

    I think this is super useful, and I too with I would have known this sooner. Thanks for posting!

  • @ziadx3
    @ziadx3 Před 8 měsíci

    bro I have been searching for a way to make conditional types, thank you!

  • @daniloochoahidalgo9783

    Just discovered this about the same time as you, it seems to be. Good video!

  • @mluevanos
    @mluevanos Před rokem +4

    When I use Typescript and React, I like to create an interface and extend the HTML attributes onto it like so:
    interface MyComponent extends React.HTMLAttributes {
    // Extends props
    }
    And that will add className, name, onClick, and all other attributes related to an HTML DIV Element.

    • @runonce
      @runonce Před rokem

      Do you spread the rest of the props? Or do you destructure each prop of the native element?

  • @tarakganesh1472
    @tarakganesh1472 Před rokem +1

    Thanks man!! Have been looking for a solution for this scenario.

  • @mateja176
    @mateja176 Před rokem +15

    In the case of multiple branches, you could use “satisfies” to ensure that the keys and types do not get mixed up. For instance “satisfies { gender: string }”

    • @ExtraterrestrialCatgirl
      @ExtraterrestrialCatgirl Před rokem +3

      hey that sounds interesting, could you explain it a little bit more? :) that sounds interesting but I can't really figure out what exactly to do with the "satisfies" keyword here

    • @yousafsabir7
      @yousafsabir7 Před rokem +1

      ​@@ExtraterrestrialCatgirl yeah, maybe a code snippet would do

    • @joshtriedcoding
      @joshtriedcoding  Před rokem +3

      the satisfies keyword is super interesting too. Gonna look more into this typescript stuff, it's actually cooler than I thought

    • @MrMoonCraft
      @MrMoonCraft Před rokem

      @Joshtriedcoding1 love the realization 5 hours later 😂😂

  • @richardjamesbunker
    @richardjamesbunker Před rokem

    Awesome video. Very clear and helpful. Thank you!

  • @emee__
    @emee__ Před 11 měsíci +1

    Bro you just saved me right now, I was searching for this 👍👍👍

  • @OleksandrBorysenko333
    @OleksandrBorysenko333 Před 11 měsíci

    Nice trick! Thanks for sharing.

  • @santicanabalramos667
    @santicanabalramos667 Před rokem +2

    Great video! By the way, in the conditional statement, because there are only 2 options for the gender property, you can turn the else if into just an else. Conditional types are one of the most powerful features in typescript. I love your videos, keep going 🙂

    • @CyberMew
      @CyberMew Před rokem

      What do you mean? Do you have a small example of it?

  • @harmmeijer6582
    @harmmeijer6582 Před 11 měsíci

    Nice video, thank you. You could add exhaustive switch on union types when using it:
    export function matchGuard(_: never): never {
    throw new Error('Unreachable code in match guard')
    }
    In code using your person
    if(props.gender==='male'){
    return ''
    }
    if(props.gender==='female'){
    return ''
    }
    matchGuard(props)
    If you add another gender later the line "matchGuard(props)" will give you a compile error.

  • @Alan-tx6et
    @Alan-tx6et Před rokem

    that's something that i needed really much
    thanks

  • @mohamedzaheen3246
    @mohamedzaheen3246 Před rokem +1

    Great job josh

  • @torickjdavis
    @torickjdavis Před rokem +1

    This is an extremely useful feature in TypeScript. Often the usage of a union type (separated by a pipe "|") that can be distinguished by a literal value is known as a "discriminated union". Researching that is extremely helpful for finding use cases.

  • @KerimWillem
    @KerimWillem Před 7 měsíci

    Very useful, thank you!

  • @xReDxTuRtLeZx
    @xReDxTuRtLeZx Před rokem

    ahhhh i recently did something similar but used if statements. this is so much cleaner and makes more sense. def a good tip

  • @adammilner4512
    @adammilner4512 Před rokem

    Bro huge thanks to you , i comment really rare, but the information you shared in this video helped me so much

  • @Jrrs2007
    @Jrrs2007 Před 11 měsíci

    HELL YES!! Nice video!!

  • @Axorax
    @Axorax Před měsícem

    Thanks! Great video

  • @krishnayallapanthula
    @krishnayallapanthula Před 7 měsíci

    Great explanation. Thank you.

  • @Saturn2888
    @Saturn2888 Před rokem

    I literally just recommended this method at work today. Since I use only types and not interfaces, I had to come up with creative ways to do conditional types.

  • @ahsanimam5243
    @ahsanimam5243 Před 11 měsíci

    Very well done Sir! Thank you

  • @Alluriah
    @Alluriah Před 11 měsíci

    sooo cool and intuitive!

  • @gautamjh
    @gautamjh Před 5 měsíci

    Great video!

  • @Matt23488
    @Matt23488 Před 11 měsíci +1

    TypeScript is an incredibly powerful language for describing your APIs with very specific types. It's one of my favorite languages because of this.

  • @baka_baca
    @baka_baca Před rokem

    This was clearly explained and feels useful 🎉

  • @kevinc4736
    @kevinc4736 Před rokem +1

    Thank you sir! Gonna be able to use way less question marks now

  • @gabrielkyomen4782
    @gabrielkyomen4782 Před 11 měsíci

    Wow. I was trying to google this for weeks and didn't quite got the correct terminology. Thx for sharing!

  • @justin_t
    @justin_t Před 8 měsíci

    very neat trick! please show more videos!

  • @samueldonsmog6006
    @samueldonsmog6006 Před 9 měsíci

    This is very helpful. Thanks

  • @devklepacki
    @devklepacki Před rokem +2

    @joshtriedcoding It's possible to destructure it in Props! You can use weight?: never and salary?: never for the other Type. So f.ex. on MaleProps it will be { gender: string, salary: number, and weight?: never }. This way all properties will exist on all PropTypes and destructuring will be possible. Both salary and weight will be typed as number | undefined and you can easily typeguard them if needed.

    • @joshtriedcoding
      @joshtriedcoding  Před rokem

      Oh interesting. Weird that they don't show up in the intellisense

    • @devklepacki
      @devklepacki Před rokem

      @@joshtriedcoding they do once you add `weight?: never` / `salary?: never` as I described :) Only then they will "always exist" and intellisense will pick them up

  • @developerpranav
    @developerpranav Před rokem +13

    0:16 I like how you're using components from other file without even importing them. That's more magical to me

    • @parlor3115
      @parlor3115 Před rokem +2

      Unless he's using a plugin that manages imports behind the scene, I'd say this is because of a bug in VS Code that suppresses import errors when the target isn't exported. TypeScript won't compile it in this case, though.

    • @joshtriedcoding
      @joshtriedcoding  Před rokem +1

      yeah NO idea why this is, my vscode just started doing it. based

    • @mhmdalharbi2370
      @mhmdalharbi2370 Před 11 měsíci

      Because both child and parent are not modules, so Typescript treat them both as globals

  • @Kay_Drechsler
    @Kay_Drechsler Před 11 měsíci

    Awesome trick and well explained! Could you maybe elaborate on the VSCode keyboard shortcuts you are using to destructure types, to see necessary types and the like?

  • @majidghafoorzade9906
    @majidghafoorzade9906 Před rokem

    Very useful video. thanks 👌

  • @KevinVandyTech
    @KevinVandyTech Před rokem +3

    Just 4 days ago, I figured out something very similar for my OSS project. I figured out how to make an xor type that is like this, but will require props from the 2 different set of types to be mutually exclusive. Just 1 set of props or the other, not all.

    • @dinoDonga
      @dinoDonga Před rokem

      Care to share?

    • @Ultrajuiced
      @Ultrajuiced Před rokem

      Something like
      {
      a: number;
      b?: never;
      } | {
      a?: never;
      b: string;
      }
      ?

    • @KevinVandyTech
      @KevinVandyTech Před rokem

      @@Ultrajuiced I've tried responding but my youtube comments are always deleted. oh well

  • @xingfucoder2627
    @xingfucoder2627 Před 7 měsíci +1

    Great content! could you make a TypeScript series about all those advanced concepts? Would be great. Or maybe a course will be appreciated.

  • @XON532
    @XON532 Před 11 měsíci

    TypeScript is going to be yummy han anytime. Appreciate sharing the valuable tip.

  • @sire_ns
    @sire_ns Před rokem +2

    Need more such videos

  • @PatrickMeppe
    @PatrickMeppe Před 9 měsíci

    I finally had a use case today where I applied this approach. 🙂

  • @rembautimes8808
    @rembautimes8808 Před 7 měsíci

    Thanks for sharing. Useful tool in reducing errors and leveraging intelisense.

  • @petrblinkov508
    @petrblinkov508 Před 11 měsíci

    You can setup ApiResponse or ApiResponse for Error response and allow to use it as ErrorApiResponse in any Api not only for number

  • @suryak3040
    @suryak3040 Před 10 měsíci

    Nice explanation man 👍.

  • @buddy.abc123
    @buddy.abc123 Před rokem +1

    Let me go refactor my code, this is exactly what I need earlier. Thank you Josh

  • @kingsleykelechionwuchekwa7508

    Ohh that's cool😮. Thanks for the trick

  • @einargs
    @einargs Před rokem

    Typescript has some truly awesome tools for building types. I cannot recommend reading the full docs enough.

  • @joonesmusic
    @joonesmusic Před 8 měsíci

    Awesome, thank you!

  • @petarkolev6928
    @petarkolev6928 Před rokem +1

    Wow, you got an insta sub from me, man, super video!

  • @misterl8129
    @misterl8129 Před rokem

    this is pretty good actually, thanks!

    • @misterl8129
      @misterl8129 Před 6 měsíci

      this is in the documentation?¡ :o

  • @ryangamv8
    @ryangamv8 Před rokem +2

    I just recently learnt about the TS "in" keyword. It's also really useful for union types where you don't have a discriminator like the "gender" field in this example. You should do a vid on that too!

    • @joshtriedcoding
      @joshtriedcoding  Před rokem +1

      Gonna look into it, thanks for the suggestion dude

    • @DubiousNachos
      @DubiousNachos Před rokem +1

      That's not exclusive to TypeScript. JavaScript has it, too

    • @ryangamv8
      @ryangamv8 Před rokem

      @@DubiousNachos huh yeah TIL. It does really help specifically in TS cos of what I said above. That is you can't even do something like 'if (person.salary === undefined)...' in the above case but you can use the 'in' keyword

  • @damiantriebl1747
    @damiantriebl1747 Před 11 měsíci

    good tip!, this tips are super usefull!!

  • @ZiRo815
    @ZiRo815 Před 11 měsíci +2

    The video uses React which makes the example a bit awkward because I’d recommend against coupling your display components to conceptual types - try to keep React components generic, dealing with the display of the data, not complex types. Why? Otherwise your display becomes tightly coupled to your complex types, and you end up needing to pass in a whole ComplexPerson to get a SmallWidget component to work. You may have felt this when you needed to add a new property to a mock in a component that doesn’t even use the property you just added. This actually also applies to non-React functions too. A useful question is “is it really a Person that I need to pass in here? Or am I dealing with something that just kinda looks/feels like a Person?” Coupling is one of the biggest pains to deal with when writing software and ideas like this can help you avoid it.

    • @patrickeriksson1887
      @patrickeriksson1887 Před 7 měsíci +1

      this person knows what they’re talking about. this is a great point for reusability and testability.

    • @ZiRo815
      @ZiRo815 Před 7 měsíci

      @@patrickeriksson1887 if you know, you know. I’ve been coding for 20 years now 🙈

  • @DennisPeters39
    @DennisPeters39 Před rokem

    Junge, heute noch das Problem gehabt und zufällig haust du nen Video raus. Danke dir 👍

  • @Black_void375
    @Black_void375 Před 10 měsíci

    nice video man

  • @shinshinremix6324
    @shinshinremix6324 Před rokem

    Nice! Thank you very much

  • @bigjamar
    @bigjamar Před 10 měsíci

    Muchísimas gracias, excelente!!

  • @buddikagunawardena2200

    Awesome video

  • @dominikalkhovik8537
    @dominikalkhovik8537 Před rokem

    Cool video, I was wondering what shortcut/ extension was used when clicking on the type to have it added to the deconstructed props.

  • @ThanHtutZaw3
    @ThanHtutZaw3 Před 10 měsíci

    so I can use this for passing same props with different types ? example passing files that can be files type and custom file type . Currently using 'as' when passing props and using props.

  • @bollo_omar
    @bollo_omar Před rokem

    This is wonderful

  • @axiinyaa
    @axiinyaa Před rokem

    i don't even program with typescript but i'm so interested in these kind of things

  • @rajfekar1514
    @rajfekar1514 Před rokem

    Great 😃👍 I learn something new in typescript.

  • @hsul-git
    @hsul-git Před 11 měsíci

    But what if you use prop destructuring to check if all needed props ar given? I get a does not exist on that, because not in every case its present. What we do here then? Conditions in the destructure are not allowed. So this approach only work when using unspecified, nonchecked props?

  • @John-Dennehy
    @John-Dennehy Před rokem

    I've previously achieved this with Never type, but not seen this approach before. Interesting

  • @badname110
    @badname110 Před 11 měsíci

    lovely one

  • @igboanugwocollins4452
    @igboanugwocollins4452 Před 5 měsíci

    Thank you for this

  • @devinjameson352
    @devinjameson352 Před 7 měsíci

    Neat! I might suggest starting with something super explicit like this introducing additional complexity to the type system as needed.
    type Props = {
    person: Person
    }
    type Person = Male | Female
    type Male = {
    kind: "Male"
    name: string
    salary: number
    }
    type Female = {
    kind: "Female"
    name: string
    weight: number
    }

  • @Nabulio85
    @Nabulio85 Před 9 měsíci

    Awesome awesome awesome.
    Thx ! 😊

  • @LuKaSSthEBosS
    @LuKaSSthEBosS Před rokem

    Similar behavior is when u use union type of two interfaces, then check with keyword "in" is property in object

  • @loO5r
    @loO5r Před 6 měsíci

    Really cool trick!
    Downsides I see compared to the "classical" way with Interface Male extends Person {...} is:
    - it is harder to read for other team members (React novices or even non React developers)
    - it works only binary. As soon as you have more than two options, you need to refactor the code anyway
    (and no, I didn't meant to start any gender discussion rn)

  • @apanchuk
    @apanchuk Před 11 měsíci

    1:22 how did you do this, click on property name in the interface and got it desctructured in props in the component?

  • @0xpatrakar
    @0xpatrakar Před rokem

    This guys points out some really obvious things that I have been using for a while but looking at comments apart from 2 or 3 comments everyone finds these tips useful. It leaves me baffled.