Generics: The most intimidating TypeScript feature

Поділитися
Вставка
  • Опубліковано 22 гру 2024

КОМЕНТАРІ • 243

  • @phamtienthinh1795
    @phamtienthinh1795 3 місяці тому +2

    Man, being able to find this channel while learning TypeScript is a true blessing.
    I'll will alway looking for this channel when having problems with Typescript. Thank you Matt

  • @DjokovicAirlines
    @DjokovicAirlines Рік тому +85

    Matt, you are the Wizard. I can't describe how helpful this video was for me. It's purely golden. Thank you for your work!

  • @Deliverant
    @Deliverant Рік тому +14

    Please never stop doing these videos, you're my inspiration

  • @nikostsakas7742
    @nikostsakas7742 Рік тому +13

    The stuff about apis is golden, I recently did a similar thing to stop typing JSON responses as 'any' and it really helped with leveraging Typescript to handle edge cases etc. I haven't used schema validation yet, but seeing how clean the code is, it's really enticing.

  • @shivamjhaa
    @shivamjhaa Рік тому +1

    This is actually really awesome. The light bulb moment for me was "Take generic from inward to outward": as in, generics can be inferred from the arguments passed to a function. This is very poweful

  • @stonecoldcold2941
    @stonecoldcold2941 Рік тому +1

    One of the best videos out there on Typescript Generics!

  • @emiletremblay1377
    @emiletremblay1377 Рік тому +2

    that's it im sold. Subbing to this gold channel

  • @concisedeveloper
    @concisedeveloper Рік тому +16

    10:07 To get the proper return type of your function, instead of type assertion, you can add it as return type to the function:
    const typeObjectKeys = (obj:TObj):Array =>{
    return Object.keys(obj)
    }
    This would result in the perfectly valid error of "Type 'string' is not assignable to type 'never'", since empty arrays can be passed to it. Since keyof keeps the original type but Object.keys returns strings, this can cause runtime errors.
    The proper implementation would assure that only objects with valid keys can be accepted by the function:
    const typeObjectKeys = (obj:TObj):Array =>{
    return Object.keys(obj)
    }

  • @brangtoggez6363
    @brangtoggez6363 Рік тому

    I love you Matt, free contents that you produced are chef kisses. I am always craving for more.

  • @ori_geva
    @ori_geva Рік тому +4

    Hey Matt, your videos are very helpful, you're covering features in TypeScript I needed and didn't know exist.
    It'd be so great if you could give and example of a scenario before explaining a TS solution, for the more complicated ones.
    Leaving out inline object types (like { firstName: string, lastName: string } would be nice too just so there's less to process heh.

  • @VinitNeogi
    @VinitNeogi Рік тому +64

    Which extension is it which is giving type hints using "^?"

    • @dentalfloss1
      @dentalfloss1 Рік тому

      Yup I would love to know this also

    • @hyperprotagonist
      @hyperprotagonist Рік тому +27

      twoslash-queries vscode extension.

    • @_timestamp
      @_timestamp Рік тому

      @@nextmaker Thank you for this link :D

    • @efkastner
      @efkastner Рік тому +1

      Hah nice, Matt just posted a video about this extension!

    • @ChillAutos
      @ChillAutos Рік тому

      Chdck his most recdnt video

  • @TeaBroski
    @TeaBroski Рік тому +10

    Thanks for sharing, great tips and I didn't know much about generic types.
    I just fear huge overheads, especially in the code readability department. When these types of codebases are written in real world scenarios, things turn pretty ugly pretty quickly.
    And usually, when the wizard who wrote this dark spells on a magic mushroom microdose induced flow state goes for the better paying company, the rest of people is left scrambling in chaos.
    Sometimes, especially for large companies with mixed teams, it's ok to be a bit verbose and repetitive if it leads to faster handovers and a better overall team experience.
    That said, we (as the JS community) need to mature towards more advanced design patterns.
    Was great learning about generic types today, thanks for sharing!

    • @RmonikMusic
      @RmonikMusic Рік тому

      In general, you'll mostly see generics, conditional types, the "infer" keyword, etc used in packages to make them as strictly typed but broadly usable as possible. In real-world production code, I would indeed avoid complex types and be more verbose about your typing.

  • @BlurryBit
    @BlurryBit Рік тому

    The most helpful video I have seen for a while. :)
    Adding to bookmark for using as a documentation.

  • @kevduc314
    @kevduc314 Рік тому +1

    Amazing video! Thank you!
    One question tho, for tip 7 (10:07) wouldn't casting as Array potentially give wrong types (and thus potentially wrong linting) if some of the keys of the object are not defined as strings? Since Object.keys returns strings and even if the key is not a string (e.g. number) it's converted to a string, but `keyof TObj` doesn't convert to string and keeps the original type. In the example, that problem doesn't come up since both the "name" and "age" keys are strings, but for e.g. { name: "John", 42: "whatever value" }, if you later try to do some string-specific operation on `result`, like `result[1].charAt(0)`, the IDE will flag it as something like `Property 'charAt' doesn't exist on type '"name" | 42'`, when that's a perfectly valid thing to do since Object.keys will return the string "42" for the number key 42

    • @anush8
      @anush8 Рік тому

      You're right.

  • @nikhilgeorgemathew791
    @nikhilgeorgemathew791 11 місяців тому

    best video i have seen in a while on yt. thankyou!

  • @piano42
    @piano42 Рік тому

    13:42 From a C# background `TKey extends keyof TObj` makes no sense for me. Why does this describe the `typeof TValue`?
    I hope I can internalize the solution because I stumble over this problem from time to time and asking myself again and again how the syntax was supposed to be. ^^

  • @F4LLEND4RK
    @F4LLEND4RK Рік тому

    Brilliant video, learnt a lot - I've never really looked into generics before, so, now my Typescript knowledge really has gone up a level.

  • @MartynK981
    @MartynK981 Рік тому +2

    @Matt, do you know why in example N8 (getValue function) we need explicitly provide the second generic for the keys - 'TKey extends keyof TObj'? Why the setting of the arg 'key: keyof TObj' is not enough?

    • @jasonstewart7983
      @jasonstewart7983 Рік тому

      I'll attempt to answer in the absence of Matt. The issue is how to get TS to connect the return type with the input type. Since the input type is an object with multiple attributes, each with a different type, we don't want the return type to be a union of all those possible types, we want it to be keyed to the 'key' input arg. Hence we use the 2nd generic and infer the return type. On a deeper level, I'm uncertain "why" this is necessary, why TS didn't have enough information to start with. Which is something for Matt to explain.

    • @MartynK981
      @MartynK981 Рік тому

      @@jasonstewart7983 Thank you

  • @KhalilCodes
    @KhalilCodes 8 місяців тому

    Hey Matt,
    I've been following you on X for sometimes and learned a lot from about TS.
    And here as well.... Subscribed 😍

  • @i4o
    @i4o Рік тому

    Coincidentally, I just watched your generics video on LWJ and was doing those exercises. This is a great video. Lot of things clicked for me with generics! Thanks Matt!

    • @andyl.5998
      @andyl.5998 Рік тому

      What's LWJ, if I may ask?

    • @kidussolomon9708
      @kidussolomon9708 Рік тому

      @@andyl.5998 Learn With Jason www.youtube.com/@learnwithjason

    • @aaronmiller1835
      @aaronmiller1835 Рік тому

      @@andyl.5998 Learn With Jason, if you haven't found it already

  • @juansereina_
    @juansereina_ Рік тому

    The more I watch your videos I love TS more! 🧡

  • @artless-soul
    @artless-soul Рік тому

    Thank you Matt for all this great content! May be consider slowing down a bit so we get time to grasp the content and avoid head spinning injury by having to rerun videos/ play at lower speed with robot voice 😀

  • @lasfito
    @lasfito Рік тому

    In example number 6, why do TObj extends Record when a mere Record would've sufficed?

    • @mattpocockuk
      @mattpocockuk  Рік тому +1

      Because you wouldn't get autocomplete on the key that comes back in the return type

  • @LucaStasio
    @LucaStasio Рік тому +1

    Man, you are the sunshine!

  • @Ageofzetta
    @Ageofzetta Рік тому

    Straight to bookmarks.
    You're an amazing teacher!!

  • @ivankudinov4153
    @ivankudinov4153 3 місяці тому

    That number 8 tip was, in fact, really good, I didn't understand it before

  • @Mitsunee_
    @Mitsunee_ Рік тому +2

    everytime I try to use keyof in generics I end up with it staying at the union type. Yesterday I was trying to merge all my getBundledFooBar functions into a single util and it just never wanted to work for me. Really confused as to what specifically makes typescript understand when a keyof-based parameter is not the union of all keys.

  • @alimoghadam6464
    @alimoghadam6464 Рік тому

    I'm in love with that makeZodSafeFetch function! Thank you for this great video!

  • @eulucascampelo
    @eulucascampelo Рік тому +2

    14:03 is the better part of this video 😁

  • @AlexKozack
    @AlexKozack Рік тому

    How to make type argument required? That createSet() should throw a type error about missed type argument?

  • @justingolden21
    @justingolden21 Рік тому

    This man is on another level! Subbed!

  • @chrisjames278
    @chrisjames278 Рік тому

    Thanks, Great video! Look forward to going through it in total typescript

  • @luczztem
    @luczztem Рік тому

    bro this is wonderful you explained it VERY well

  • @sandeepreddy6247
    @sandeepreddy6247 Рік тому

    this is absolute masterclass...

  • @jcubic
    @jcubic Рік тому +2

    That was great but you did one mistake in tip 7 you don't need to use "as" in that example if you restrict the type of the object to have keys of type string. Object.keys doesn't return symbols in JavaScript but the keyof does.
    const typeObjectKeys = (
    obj: TObj
    ): Array => {
    return Object.keys(obj);
    };

  • @ChillAutos
    @ChillAutos Рік тому +2

    This was a brilliant video. You are clearly incredibly knowledgeable in all things typescript so instead of just giving praise I'd offer a tiny bit of criticism, maybe it's worth something too you, maybe not.
    Ive watched all your videos, some multiple times, and the one thing I notice is that some examples are a little too contrived, or at least it's not clear where I'd ever use a certain tip. Ive been a professional ts dev for about a year, and I'm no where near as good as you are but I really struggle to see the use cases in some of the tips, I'm not saying they don't exist, it's just I can't see them. Your zod example is a perfect example of the type of tips I find most useful, it's clear the real world use case and the example you use is a something we might actually see in our own code bases. Basically I'm saying is love some more real world examples for some of the more complex tips you give. I think part of it is your older videos were quite short which are great for a quick look and you do a great job of explaining how it works clearly, but then the video just ends and I'm left wondering how to apply it somewhere.
    Overall 9/10, just thought this might be more useful just another clap emoji. Thanks for your videos, looking forward to the next one as always.

    • @mattpocockuk
      @mattpocockuk  Рік тому

      That's interesting! I've heard this criticism before but I consider this video to be a good example of me using real-world examples. Type safe object keys, typing Set, making type-safe fetches, integrating with Zod are all things I've been asked how to do. Which example in particular felt too abstract for you?

    • @mshamba8066
      @mshamba8066 Рік тому

      good feedback ≠ criticism

  • @onlywei
    @onlywei Рік тому

    This video is exactly what I needed!
    P.S. it’s still really hard for me to get over the fact that non-Chinese people don’t know how to count to 10 on one hand.

  • @erikslorenz
    @erikslorenz Рік тому

    Tip 10 is the best one! Just did something similar the other day.

  • @christopheanfry
    @christopheanfry Рік тому

    So nice video!! I’m starting with typescript so not sure if I get it well but what I understand is that generics help typescript to infer on the arguments we pass in a function and give the proper return type. So a well defined generic in a function will give a much easier function handling when using it. Sorry if it’s not clear for me just try to understand. Thanks anyway for this video will work on it.

  • @jonatanferenczfi189
    @jonatanferenczfi189 Рік тому +1

    Great video. Not many new things for me. But it's really good to see someone making truly advanced topic videos on TS.
    Keep it up 🚀

  • @lamtran6520
    @lamtran6520 10 місяців тому +1

    Question: What is the // ^2 in your code, and why it log the variable all the time ?
    Is it a plugin or typescript external ?

    • @joker_j1268
      @joker_j1268 9 місяців тому

      Its a VSCode extension. He covered it in this video before:
      ua-cam.com/video/u0adKDu--cA/v-deo.html

  • @vaidassiburskis
    @vaidassiburskis Рік тому

    Nice work Matt

  • @stiwart83
    @stiwart83 Рік тому

    Amazing video Matt, thank you so much!

  • @learner8084
    @learner8084 Рік тому

    Thanks very much for the video. This is a real eye opener.

  • @bryanngen5572
    @bryanngen5572 Рік тому

    I usually don't make comments, but this was a great video. Thank you !

  • @UsernameUsername0000
    @UsernameUsername0000 Рік тому

    7:43 Any difference between doing that and doing extends Function?

  • @JulianHarris
    @JulianHarris Рік тому

    Outstanding. Last time I used generics was C++ and Java in the late 90s probably before most of the other commenters were born😂 and it’s really great to see the inference capabilities to avoid “type stuttering” you otherwise get.

  • @alpakarosa3678
    @alpakarosa3678 Рік тому

    This video makes me love more typescript!

  • @theaungmyatmoe
    @theaungmyatmoe Рік тому

    I just love final part imo

  • @ofekhamdi8743
    @ofekhamdi8743 Рік тому

    Hey Matt, another great video! In the end of the video you still have to create a zod object of the type specified. Can you make zod infer the object from the schema and implement it?

  • @robwatson826
    @robwatson826 Рік тому

    I pressed the like button, I got a surge of dopamine 👍. Really interesting video, generics are fun!

  • @WaterJay
    @WaterJay Рік тому

    this video is just immensely helpful

  • @mohdsahil226
    @mohdsahil226 11 місяців тому

    Really great content! Please provide a Nextjs 14 course. I follow stephen grider, but your way of teaching impressed me.
    I would like to work for free for you! I want to improve my coding!

  • @FranciscoLopez-jc6vq
    @FranciscoLopez-jc6vq Рік тому

    Great video, Matt. Hats off ;)

  • @bagaskara1567
    @bagaskara1567 Рік тому

    very clear explanation, amazing

  • @brandonz404
    @brandonz404 Рік тому

    This video singlehandedly dissolved my fear of "complicated" typescript. I'm pretty new to it, but now I want to use generics everywhere

  • @rkingham3181
    @rkingham3181 Рік тому

    hmm...
    Sorry for my ignorance but re. tip 6 what is the point of and then "let highestKey: keyof TObj" when we can do "function(obj: Record) and then (obviously) "let highestKey: string" - that also takes advantage of Tip 4? Why should we do it the painful way?

    • @mattpocockuk
      @mattpocockuk  Рік тому

      Because then the highest key would be too loose as a type, and you wouldn't be able to index into the original object with it. Having keyof there means a nicer API for those consuming the function.

    • @rkingham3181
      @rkingham3181 Рік тому

      @@mattpocockuk I think I can understand the philosophy, but am I wrong to think that with the Record the "keyof" will have no way to be anything but string anyway?
      In other words should we sacrifice our knowledge that a particular type 'has to' be - say - string, for the sake of writing a more sophisticated looking code?
      Thank you for taking the time to reply mate!

    • @mattpocockuk
      @mattpocockuk  Рік тому +1

      @@rkingham3181 The point here is that keyof T is _more_ specific than string. I.e. it's a specific set of literals.

    • @rkingham3181
      @rkingham3181 Рік тому

      @@mattpocockuk ah...
      I think I got it now!

  • @thohangst
    @thohangst Місяць тому

    I don't know what it is about generics, but they just make my brain shut off. Looking forward to remedying this with this video.

  • @celica96
    @celica96 Рік тому

    Many cool pieces of advice. Thank you!

  • @eliasmangoro413
    @eliasmangoro413 Рік тому

    Nice video! Another tip is you can do generic type inference from a function return type, in the same way you can do it for function parameters

  • @msich
    @msich Рік тому

    This is awesome Matt 🙌🏻

  • @multigladiator384
    @multigladiator384 Рік тому

    13:34 nice! this is useful stuff

  • @eldbudd
    @eldbudd Рік тому

    what is this addon with ?^ that shows the typing of the consts as they change according to the different types that are fed to the generic type?
    for example that ^? const result: ...." with the changing types.
    anybody?

  • @Praetorsss
    @Praetorsss Рік тому +1

    I have the task to write a blog post about generics and you, my lord, have completely saved me in the most important thing to do it, understanding them!

  • @DanielAbernathy
    @DanielAbernathy Рік тому +1

    One thing I don't understand - why does TS use inference here:
    `(obj: TObj, key: TKey)`
    But not here:
    `(obj: TObj, key: keyof TObj)`
    Aren't we essentially providing TS with the same information?

    • @oduylinh5773
      @oduylinh5773 Рік тому

      I have the same question. I can't understand why it has this difference

    • @mpotane
      @mpotane Рік тому

      probably the return type becomes a union because it split the inner keys in the object itself.

  • @lukadimnik1554
    @lukadimnik1554 Рік тому +1

    At 14:00 I laughed so hard. Really good explanation of generics. Good job.

    • @richards16
      @richards16 11 місяців тому +1

      Yeah right, the guy really likes Generics LOL!

  • @Noway1252
    @Noway1252 Рік тому

    What a wonderful video! I wish I can like it more than once. Thanks a lot Matt

  • @alexodan
    @alexodan Рік тому

    amazing stuff Matt thanks for those tips🙌 they were really 🔥🔥🔥

  • @eldbudd
    @eldbudd Рік тому

    i wish i'd seen this video like four months ago, when i refactored an entire routing architecture, for which i found myself using what here you will see as tips 1 through 9.
    Had to learn it the harder way. this video would have really made the learning curve less steep.
    I

  • @maxtsh
    @maxtsh Рік тому

    Love this Matt, thank you so much

  • @nice-vf4rj
    @nice-vf4rj Рік тому

    Hi Matt, great content as always, may I ask what extensions or settings you have that allows inlay hints to popup where there is "// ^?"

  • @petarkolev6928
    @petarkolev6928 Рік тому

    Default params in TS o.O Whaaaaaaaaaaaaaat ? Matt, tip after tip and I think you can't amaze me more and there you go :D Matt, you're our hero 🍻

  • @jainebri
    @jainebri 9 місяців тому

    Amazing Matt ... Just Amazing !!! Thanks

  • @jeffreysegovia7656
    @jeffreysegovia7656 Рік тому

    Thank you Matt!

  • @amans6504
    @amans6504 Рік тому

    Can you do one with axios? I literally get confused using axios with typescript and make my responses and errors typesafe

  • @maker_aleks
    @maker_aleks Рік тому

    Really good explanation! 👍

  • @carlosricardoziegler2650
    @carlosricardoziegler2650 Рік тому

    Nice content, we are using it a lot with react applications. I will ask why in part of generic we need to write like instead a simple . Thanks for sharing your knowledge:)

    • @mattpocockuk
      @mattpocockuk  Рік тому +2

      Because in .tsx files is parsed as a JSX node, but isn't.

  • @aram5642
    @aram5642 Рік тому

    Hi, is there any difference between 'object extends T' and 'T extends object'? If so, what could be an example to best illustrate a use case for one vs the other?

  • @eliasvasques80
    @eliasvasques80 Рік тому

    Thanks man, very good content.

  • @joelmturner
    @joelmturner Рік тому

    Excellent tips, thank you!

  • @4sent4
    @4sent4 Рік тому

    Default type parameter is actually genious feature

  • @patricknelson
    @patricknelson 11 місяців тому

    Like and subscribed. Awesome tips, they proved to be particularly useful for me just to learn.

  • @todorvachev891
    @todorvachev891 Рік тому +1

    Example 4 I think is not a really good idea, especially if you are working on a team or a big project, might work well for small stuff. The reason is simple, you wouldn't really write addIdToObject( ... ). You would normally have that out in a type or an interface, so it would be more like addIdToObject({ ... }), and now if you are using IdType anywhere else in the code, if you change it, you will get all of the errors that you need. However if you have left the type as inferred, you could simply say pass in an extra argument to the function, or remove an argument, and you would never know that you just broke 50 different things until it gets to production and you get angry clients :)

  • @skittlznt2611
    @skittlznt2611 Рік тому

    Thanks for the videos Matt. I can imagine they take a lot to put together. Just something I've noticed, you speak really fast and take almost no breaths between words 😅 then you go from one topic to the next with almost no breaks in-between. For me personally It's really tough to follow because these are kind of complicated topics you are covering. Again, I really appreciate the time and effort you put into your videos. Just a bit of constructive feedback from one viewer. 👌🏾

  • @firasnizam
    @firasnizam Рік тому

    great, direct into the point

  • @m_hussain_mustafa
    @m_hussain_mustafa 8 місяців тому

    Great video!

  • @PickleRiiiiiiick
    @PickleRiiiiiiick Рік тому

    Did you invent Typescript? Lol. Just kidding. I watch your videos all the time. Very enjoyable.

  • @webdev5180
    @webdev5180 Рік тому

    I love this video so much !

  • @oussamabenchkroune3151
    @oussamabenchkroune3151 Рік тому

    Thank you for the effort to make this awesome and useful video, Much love

  • @ccbio2981
    @ccbio2981 Рік тому

    in example 2, wouldnt it make more sense to just "hard"-type the return type of promise? It makes the function and the function call much more readable

    • @mattpocockuk
      @mattpocockuk  Рік тому +1

      Ish - by making it generic you leave open the possibility of TS being able to infer cool stuff from it. It's a bit of a contrived example, albeit one that is based in code that I've seen in the wild (in cal.com's repo, for instance). It's really a way of setting up exercise 4 and 10.

    • @mattpocockuk
      @mattpocockuk  Рік тому +1

      Also, doing it my way means that the resulting type (if you don't pass a type argument) is unknown, not any, which is more type-safe.

    • @ccbio2981
      @ccbio2981 Рік тому

      @@mattpocockuk i think what threw me off a bit is that these two functions are directly below eachother. Thinking about it more, make fetch is probably some sdk you import to get data (so something the libary auther writes), and you use the generic function argument to type your output. looking at it like this it makes sense ofc to do it your way - and you are right, there are examples all over (for example the pockerbase javscript sdk). thanks for the reply.

  • @rafadydkiemmacha7543
    @rafadydkiemmacha7543 4 місяці тому

    Is there any difference between (...args: any) => any and () => any for TypeScript?

    • @mattpocockuk
      @mattpocockuk  4 місяці тому

      Yes, the first one indicates any number of parameters. The second indicates no parameters.

    • @rafadydkiemmacha7543
      @rafadydkiemmacha7543 4 місяці тому

      @@mattpocockuk for some reason I was sure that (a: number) => {} was assignable to () => void type, but it's not (luckily).

    • @mattpocockuk
      @mattpocockuk  4 місяці тому

      @@rafadydkiemmacha7543 Pretty sure it is, unless I'm misunderstanding

  • @Abdulrahman-zj8cv
    @Abdulrahman-zj8cv Рік тому

    Thank you so much man you are great, bless you.

  • @muhammadmejanulhaque3305
    @muhammadmejanulhaque3305 Рік тому

    I am really glad that I started my javascript journey with Typescript. Thank you so much for the video.

  • @torontodev525
    @torontodev525 10 місяців тому

    Legend! thanks mate, the best of the best of the best :) 🙏🏼

  • @jotasenator
    @jotasenator Рік тому

    let me grab a beer to make my fears go away and jump fully into this, is ok to have fear, but I need to face it

  • @qodesmith520
    @qodesmith520 Рік тому

    Always A+ content. Thanks for this!

  • @eulucascampelo
    @eulucascampelo Рік тому

    @Matt how do you debug with comment line ? (something like `// ^?`) Looks like very useful

    • @archengell
      @archengell Рік тому

      hsy did you ever find the answer to this?

    • @vahan-sahakyan
      @vahan-sahakyan Рік тому

      @@archengell ua-cam.com/video/u0adKDu--cA/v-deo.html

    • @otiamaino2461
      @otiamaino2461 9 місяців тому

      Very curious

    • @eulucascampelo
      @eulucascampelo 9 місяців тому

      @@otiamaino2461 and ​ @arkitekos Matt made a video just about that ua-cam.com/video/u0adKDu--cA/v-deo.html

  • @mervinmarias9283
    @mervinmarias9283 Рік тому

    This seems like a more powerful form of destructuring, where instead of presetting the initial values, you enforce the types that should be returned.

  • @AlfonsoBlanco-co5ql
    @AlfonsoBlanco-co5ql 8 місяців тому

    You are so cool while explaining!!!

  • @househall
    @househall Рік тому

    Sometimes `as` is unavoidable indeed, but your example is dangerous. You can easily use it in typed function like `type TA = { a: number }; function getAProps(a: TA) { return typedObjectKeys(a); }` and its result will be typed as `"a"[]` but you could put a value of some type that extends TA as an argument, like `const b = { a: 2, b: 3 }; getAProps(b);` and get `["a", "b"]` result wrongly typed as `"a"[]`.