Be Careful With Return Types In TypeScript

  Рет қаралды 62,483

Theo - t3․gg

Theo - t3․gg

Күн бұрын

I'm sorry Prime ❤️
#typescript
THANK YOU TO ALL THE AWESOME GUESTS
- Malte / cramforce
- Trash ‪@trash_dev‬
- Ben Holmes / bholmesdev
- Josh Goldberg / joshuakgoldberg
- Dax Raad / thdxr
- Maple / heyimmapleleaf
- Alex / alexdotjs
- Tanner / tannerlinsley
- Kent C Dodds / kentcdodds
- Matt Pocock ‪@mattpocockuk‬
ALL MY VIDEOS ARE POSTED EARLY ON PATREON / t3dotgg
Everything else (Twitch, Twitter, Discord & my blog): t3.gg/links

Пікірлер: 435
@t3dotgg
@t3dotgg Жыл бұрын
Goal of this video is to end the convo. Please don't be obnoxious to me, Prime, or anyone else so we can move on ❤
@MrJester831
@MrJester831 Жыл бұрын
Doesn't this just open up a whole new convo about the shortcomings of TS type safety?
@gideonmaxmerling204
@gideonmaxmerling204 Жыл бұрын
I don't think that the prime example is a good example, as not specifying a return type did not actually solve the problem with the code, you're still returning a user with the wrong permissions, just because you changed the type definition to accommodate the error doesn't mean that the error is gone
@noctali8049
@noctali8049 Жыл бұрын
This debate was super engaging and I learned a lot! You guys should disagree publicly more often 😉
@magne6049
@magne6049 Жыл бұрын
@@MrJester831 **cough** ReScript **cough** superior/superb **cough** Hindley-Milner type inference **cough**
@daedalus5070
@daedalus5070 Жыл бұрын
I just want Mummy and Daddy to stop fighting 😭
@DylanRJohnston
@DylanRJohnston Жыл бұрын
You keep calling it “lying” because you’re mistaking Typescript’s structural types with nominal types from other languages. In your User type example, you need to learn to read typescript types as contracts about minimum functionality not a maximum bound. It means this type has at least a username and an email, but it could have more. It allows typescript code to be much more composable if you embrace the structural typing instead of borrowing nominal typing ideas from other languages.
@jorgitoaral
@jorgitoaral Жыл бұрын
I completely agree with you! To me, it's kind of frustrating to try to apply the same concepts of typed languages in typescript, where typing is just a specification of the minimal contract to be meet... The only case I'm worried about though (I should better study the theory behind it) is the case of the overridden function definition.
@Kopraaaa
@Kopraaaa Жыл бұрын
Finally, someone explained it properly.
@DubiousNachos
@DubiousNachos Жыл бұрын
I agree that structural typing is powerful, but in some cases, having additional properties and values hidden beneath the TS poses security risks. Like with an API response. If TS is telling you that you're sending an object with two properties, but you're really sending something with 15 properties with some sensitive values, it's a problem if Typescript isn't able to catch that. You can make sure to extract out the properties you intend to ship off, just to be safe, but from Typescript's perspective, that isn't going to be necessary You can do things like make sure that you're extracting out the exact properties you
@damymetzke514
@damymetzke514 Жыл бұрын
@@DubiousNachos I'd argue that relying on typescript for security is a mistake in the first place. Typescript doesn't make any guarantees at compile time, so you're at risk anyways. Also TS is not telling you that you're sending an object with two properties, it's saying that you're sending an object with at least 2 properties. This is the exact point of the initial comment.
@Kopraaaa
@Kopraaaa Жыл бұрын
@@Fixeish That would be really good, but that was not point of initial comment. That's why strongly typed languages will always be better choice for me.
@samuelgunter
@samuelgunter Жыл бұрын
just explicitly return `any`, then you'll never have to deal with losing truth
@stephanjacob17
@stephanjacob17 Жыл бұрын
type everything with any and you'll never have any (pun not intended) problems!
@noccer
@noccer Жыл бұрын
Yes absolutely, and don't forget to use password123 while you're at it ☘️
@neociber24
@neociber24 Жыл бұрын
Well Theo likes it's diagrams, and any for sure is the universe
@austincodes
@austincodes Жыл бұрын
😂
@froxx93
@froxx93 Жыл бұрын
Thanks, I hate it
@felipenascimento6118
@felipenascimento6118 Жыл бұрын
The second example is not a lie, it is correct representation of what's happening. When you declare a object type with two properties, it doesn't mean "these properties and nothing else", it means "at least these properties". The error is saying that you can't use password, not because it doesn't exist, but because it cannot trust it will always exists. This is correct behavior, since is this what allows interface overriding. I think typescript should give unknown, not a error, but that's how it works. I also miss the ability to declare a strict type, with only those properties and nothing else. Typescript has a really bad typing system, in a sense that is pretty easy to lie to it. I kinda understand it. If it weren't, it would be pretty hard to do all of those dynamic transformations of value that it allows
@JeyPeyy
@JeyPeyy Жыл бұрын
By simply looking at that code we can trust that password will always exist. You could say that explicit typing isn't a lie, but rather an omission of information, sure. But I still see it as a lie. But yes, a strict type where nothing else can be included would be a very good addition to typescript.
@ShaharHarshuv
@ShaharHarshuv Жыл бұрын
​@@JeyPeyy Omission of information is not a problem if you're not expecting the consumer to use this property. This can frequently happen on the other side with function arguments to be more flexible for the consumer. (it's considered a bad practice for a function to ask for properties it doesn't need, because than you are more strict on the consumer unnecessarily)
@kabal911
@kabal911 Жыл бұрын
It is interesting, because if we use “interface” instead of “type”, then the result of having no password property is 100% expected, and how basically every programming language works.
@ShaharHarshuv
@ShaharHarshuv Жыл бұрын
@@kabal911 There is almost no difference between interface and type in typescript, which is one of the more confusing aspects of the language
@kabal911
@kabal911 Жыл бұрын
@@ShaharHarshuv sure, and interfaces have other issues too, but that’s not really the point. My comment is about the framing of the issue. No one would say it was “a lie” if we are taking about interfaces. Everyone understands that if I have an interface called “Named” with a single property called “name”, a function that returns a “Named”, and inside that function I return an object that has many properties including “name”, then the code that calls that function only “sees” the property “name”.
@theonethatprotectsyoufromt9271
@theonethatprotectsyoufromt9271 Жыл бұрын
What I like about having a return type is that it prevents me from accidentally returning a wrong value. Most of the problems with return type can be solved if a return type is narrow. I'd still use them depending on a situation.
@TheProcessor
@TheProcessor Жыл бұрын
This has saved me many times from a small bug.
@FlorianWendelborn
@FlorianWendelborn Жыл бұрын
I can't think of many cases where you wouldn't catch those once you use the function
@TheProcessor
@TheProcessor Жыл бұрын
@@FlorianWendelborn The TS catches it faster than running the function.
@FlorianWendelborn
@FlorianWendelborn Жыл бұрын
@@TheProcessor I didn’t mean executing, I meant using the function in your code.
@SamualN
@SamualN Жыл бұрын
you almost catch this when you use the function and you notice it isn't returning the type you expect if the function is part of an api however return types make a lot of sense (and using them can prevent accidentally introducing breaking changes)
@paulomattos6753
@paulomattos6753 Жыл бұрын
4:46 the point is, the user type is TELLING THE DEVELOPER THAT "PASSWORD" IS NOT A VALUE THAT THIS FUNCTION SHOULD RETURN
@TypeScriptTV
@TypeScriptTV Жыл бұрын
Explicitly annotating return types is necessary when dealing with recursive functions: kzbin.info/www/bejne/pIfHaZ2sn7Bnj5I
@3ventic
@3ventic Жыл бұрын
I'm not a fan of the password example, because you need a test or something like Zod if you want to guarantee information doesn't get leaked in that manner; relying on seeing it with intellisense just isn't nearly good enough. The safety of accessing properties you intend to be there is still present so typescript is doing its job. The overloading is something I didn't even know typescript supported because it just seems like a terrible idea: if you need to get a specific type from a union, the answer is type-guards. The main reason I like to use explicit return types in a lot of cases is to create a contract about the function: you and future maintainers know what it's supposed to return and you can't accidentally change it, and it gives a full call signature you can share between contexts. This is important if the callstack (including network calls and other execution context changes) isn't fully type-safe, which is the case for much of the typescript code I currently write (though I'm trying to get towards full end-to-end type-safety).
@krzysztofzabawa2212
@krzysztofzabawa2212 Жыл бұрын
Exactly! I think only Matt got that right. That it depends where you use (or not) inference. Within a scope of single file or just an application code - it's totally fine. But for library code it's totally unacceptable. Password (or literally anyting else in User object) might be just an implementation detail which might be changed and we should not leak it to publicly exposed contract. Hence defining User as return type is not a lie, it's actually specifying the contract and giving a freedom of changing library function implementation details without potentially breaking implementations using it. Which translates to scalability and maintainability.
@ISKLEMMI
@ISKLEMMI Жыл бұрын
I can understand Prime's frustration with TS. I initially expected TS to be a bona fide language with a real type system, but it most certainly isn't that. It's just the best JS linter that's been built so far.
@RyanBreaker
@RyanBreaker Жыл бұрын
I seriously don’t understand why we don’t just write TypeScript interpreters, why bother still going back to JavaScript if we know it has all these problems? Of course the real solution is to just use Rust.
@JeyPeyy
@JeyPeyy Жыл бұрын
@@RyanBreaker Do you mean like a typescript interpreter that does runtime checks? Browsers will still run javascript. Breaking backwards compatibility isn't an option for them, and maintaining support for two languages isn't desirable. But for server code and WASM? Sure, just call it something else than typescript since it will behave differently.
@Slashx92
@Slashx92 Жыл бұрын
@@JeyPeyy typescript++
@NathanHedglin
@NathanHedglin Жыл бұрын
Purescript is MUCH better
@CottidaeSEA
@CottidaeSEA Жыл бұрын
With the example that Prime gave you, the issue with the code was the same regardless of explicit or inference. It correctly said what was going to be returned, but the issue was that it lied about when what would be returned. It is a pure logic error, those will always be hidden when overloading. There might be a way to solve the issue, but the point is that inferred types will simply not save you from that particular problem. As for the primary issue I can see, it's that TS doesn't validate return types correctly.
@zzzyyyxxx
@zzzyyyxxx Жыл бұрын
Yeah this seems more like an issue with TypeScript not having a sound type system, in other languages with a sound type system, you simply cannot return a type with the password but lie about it as a User type. They simply would not let you return that and would error.
@CottidaeSEA
@CottidaeSEA Жыл бұрын
@@zzzyyyxxx Yeah, the exception is the Prime example since it has the correct structure, but that could also be solved with types if TS had better checks.
@jordondax
@jordondax Жыл бұрын
But, how would that be relevant right now? To be giving the advice that you should ALWAYS do X thing? If typescript currently works in x way now, why are we trying to force a hard rule(always a bad idea) when we know typescript doesn't work this way? Btw I have no problem with Prime, I just absolutely detest dogmatic statements. And lo and behold a deeper look and BOOM, hard rule blows up in our face. IMO you can't cop out( this isn't aimed at your or even Prime just a general) that oh the language works X way, and that's bad they need to fix it during a conversation on "best" practices. We have to operate within the confines of what exists today, not another day in the future if it even is resolved.
@ПавелПитерский-д7г
@ПавелПитерский-д7г Жыл бұрын
can't agree more... IMHO the very idea of explicit return type - is a contract which function MUST fulfill. And if TS allow function in the example return user with passport prop which does not exist in the return type - it's a bug of TS... And it's really surprising if that issue does not adressed/assigned for fix... and similar bug related to function's args too... type O1 = { p1: string } const fn = (arg: O1) => arg const o2 = { p1: '', extraP: 'WTF???' } const res = fn(o2); console.log('res', res) WTF TS??
@rendezone
@rendezone Жыл бұрын
It will if you pass an “as const” parameter and now you also have “const T” to achieve that
@Rihsto
@Rihsto Жыл бұрын
Disagree about "User" example. You specify that User has at least username and email, ts does not care if the object contain more properties. But you can't return less properties. User type can be used in other parts of the system
@Rahman-xm7zm
@Rahman-xm7zm Жыл бұрын
I agree with you. Typescript does not depend on the origin of a type, when deciding if it’s compatible with another one, but it compares their members. If all the members of a type exist in another type with their appropriate types, then the two types are compatible
@AndreFera43
@AndreFera43 Жыл бұрын
I'm currently working with a lot of big, weird, any-heavy legacy codebases and I do think return types point to design decisions better than inference in most cases, especially in big functions/ methods. I would still stick with inference in smaller functions but having this tool to convey design in weird code is better I think. I wrote this comment before matt's statement, hearing it know I agree 100% with him.
@ShaharHarshuv
@ShaharHarshuv Жыл бұрын
5:16 I strongly disagree with the premise of this example. Type theory is like set theory, like a single value could be in multiple sets, so does a single value can have multiple types. In typescript, there is a concept of assignability, which basically means if type A is assignable to type B, than everything you can do with type B, you can also do to type A - so it's perfectly safe to treat a value a as type B, even if there is a stricter (narrower) type that it satisfies (like type A). This is like saying that A is a subset of B - which means that If element a is in set A, it's also in set B. Both are TRUE. In the example above, your value does salsify the "User" type definition. So it is TRUE that it's of type "User". That means that it is perfectly safe to treat this value as User, because it has all the fields you expect it to have. Aka - you're not gonna cause bugs if you do that. Type "broadening" happens all the time in typescript (and even in statically type languages) and it's one of the reasons this language is so powerful and flexible. All it does it trying to validate that you're making any mistakes (aka, not trying to read a property that doesn't exist). 6:01 You are of course correct that trying to access the password property in this case will result in an error, but your diagram misses the point completely. The circle are trying to represent the type domains of password, but in this case typescript is not even inferring the type of password. It's not telling you that the type of password is something, it's telling you that this property is not available in your object. The type inference does not happen on the "password" value, it's happening on the object value, which is broader than the "truth", which is fine (safe). A real "LIE" happen when typescript tells you some property exist when it doesn't, or a variable can only be a string when it could actually be a number. This is actually the case with the overriding example. However - I do not see any way in which typescript can fixed that without removing support for declaration overriding since it's very complicated to infer the runtime response of a function without actually running it. I have a lot of situations in which giving up on declaration overriding will make the code very annoying, like forcing me to use non-null assertion (which is less safe) / null checks redundantly (which complicates code unnecessarily). I prefer trusting the provider in this case. Types can never guard you fully. If you want to be more safe in these situations you can add unit tests.
@UliTroyo
@UliTroyo Жыл бұрын
I used to infer my return types out of laziness, assuming explicit returns were the correct thing to do. Now I get to change nothing but feel markedly superior!
@UliTroyo
@UliTroyo Жыл бұрын
This being the web, I feel I should explicitly asterisk this as a joke instead of leaving people to infer whether I'm serious.
@matejaivanovic1587
@matejaivanovic1587 Жыл бұрын
Same bro, same ahahah
@spy4870
@spy4870 Жыл бұрын
In my opinion the lie is, the Developer how says it returns x and then goes ahead and returns y. Therefore if you have honest devs, the set the return type and make the code have a proper function definition.
@christopherpoulsenfernande1624
@christopherpoulsenfernande1624 Жыл бұрын
It's not that the developers are dishonest, the point is that with explicit return types you are introducing more room for human error, which is bound to happen at some point and we want to minimize it, not increase it.
@Nil-js4bf
@Nil-js4bf Жыл бұрын
The password example didn't fully click with me. By removing the return type, you get the smallest subset of the return type possible which means you get stricter type inference downstream. I agree with this. But your example is slightly disingenuous in the way it was presented because it makes it look like it would help prevent leaking the password but that's not true because of how type compatibility works in Typescript. i.e. When you create a function logUser(user: User): void, it's going to happily accept your user object with the extra password field in it without complaining. Also lmao, I think that override example only made Primeagen more skeptical of Typescript and reinforced his love for "actual" strongly typed languages. Imo, that example was closer to what Matt Pollock would describe as a "library function". It basically improves the ergonomics by giving you a stricter type based on what you pass in. With these functions, you just have to be damn careful that your typescript definition matches the implementation of your function. Functions with type predicates are another simple example of this.
@brucewayne2480
@brucewayne2480 Жыл бұрын
If you use it as kind of TDD you write the return type of your function first then you write its body.
@Nicholas-qy5bu
@Nicholas-qy5bu Жыл бұрын
I m with prime on this one, sorry theo x) Strict typing is used to replace contract / test. I dont see the lie there since password is never used. For me this is a bad pattern, your function should not manipulate password since your code base does require any use of it. If you need to use it, update the return type with password ... About the 'yay', you could just have typed it as a const also instead of 'string'... For the rest, yes typescript has its limits, but nothing that cant be fixed with good pattern and design, or using Zod. The biggest flaw of your argument, and probably why Prime will never settle with your opinion is the fact that this does not include what happens when you want to understand / refactor the code, thats why test are usefull more than just avoiding regression. They maintain and explain the design. Same idea for explicit typings. On the short term maybe you dont see the point and i get it, but system are meant to last, and not having explicit design will harden the learning curve for new developpers and create more problem in the long run.
@ametreniuk
@ametreniuk Жыл бұрын
In the first example “yay” looks like an implementation detail, and in a general case string is acceptable. You could also make a case against returning “const” so the consumers of the function don’t rely on it. The other examples are great for highlighting of the tradeoffs of being more cautious regarding return types. I think a lot of misunderstandings regarding this topic is about “defaulting” to one strategy instead using “only” one strategy
@Szergej33
@Szergej33 Жыл бұрын
I think it depends a lot on the source of your data. In the FE working with the possibility of almost all fields can be null or undefined bc they were user generated at some point, generality is good. If the source of values or types is your own function, then const gives a lot of specificity. And consumers can depend on it, because you control the source.
@ametreniuk
@ametreniuk Жыл бұрын
@@Szergej33 agree. Specificity is great when you need it
@doc8527
@doc8527 Жыл бұрын
@@Szergej33 ​ there is one annoying thing to add on: the inconsistency of remote backend data. It's "controlled uncontrolled" source. Due to the business change and fast iteration, your strict definition can easily and quickly become a lie without notice, and break your application. In this case, the vague infer type (assumption) with undefined | null check can be useful because most of the times Frontend code will have a safe guard for them. If something happens, we might just render some empty data. Otherwise your page will be crushed due to some operations on those weird data from backend/wherever accidentally. I encounter quite amount of times frontend & backend apps were too confident about the existing defined type, and at some point the entire app was crushed unnecessarily due to those fake strict types. I see many comment keeps saying about all the strict types things are from a java/c++, pure backend background that doesn't need to handle the transaction frontend encounters. While they are being correct under their own workspaces, it's completely out of context and not useful at all. And many just failed to provide their own context. I rather hearing someone sharing examples back by its own experiences instead of randomly throwing out some concepts nowadays.
@Szergej33
@Szergej33 Жыл бұрын
@@doc8527 it's a bit messy but I think I get what you mean. And yea, i agree with inconsistent backend data. Most of the time, unless you are using trpc, when you do a fetch in the fe code you will have to manually type it, otherwise it will be any. And in those type defs you usually have to be quite vague, because of the inconsistency of the data, or backend dev added some more properties to the dto to suit another use case, on the same endpoint, what have you. But if you (in your application) own the source of the data, then using 'as const' is good, because you get both specificity and accuracy. And since the source is in your application, if you change anything, TypeScript will warn you if it breaks anything.
@Starwort
@Starwort Жыл бұрын
Additionally if you _really do_ mean that the function will always return "yay" you can just declare that as part of the return type: `Result`. You can't make that decision with inferred return types so intent is lost (and changing the implementation can easily be a breaking change - what if we change it to return "yam" instead?)
@arcanernz
@arcanernz Жыл бұрын
Return types can convey the intent of the writer whereas inference types can be utterly useless if someone decides to return an "any"; and inferences can still be wrong if you were to do "return blob as User" when blob is not really User. But that being said it's a preference whether you prefer correct but possibly useless types or incorrect but meaningful types. I actually do use inferences most of the times but I do use return types for functions where the return types are not meant to be dynamic.
@abhinavrobinson2310
@abhinavrobinson2310 Жыл бұрын
Me: can we have types at home? Mom: We have types at home. Types at home: const notX = x as unknown as any; 👻
@chimp1nski
@chimp1nski Жыл бұрын
OBJECTION. If you want a specific result truth you should use Result as return type. This way the value is typed according your expectation BUT the return type does what it's supposed to do imho: It helps you develop the actual fn that your typing. Like a little tiny test it tells me what my "return goals" are whereas "as const" is just a bandaid that types the fn from within its implementation.
@chimp1nski
@chimp1nski Жыл бұрын
Before you all go up in flames add the following: const random = Math.random(); if (random > 0.4 && random < 0.3) { return random } Hf when some junior decides to add something like that. With "as const" you cant catch that in advance - happy bug hunting. With the method mentioned above, ts will scream at you as soon as you try to return anything else than the typed values. Have a nice day
@chimp1nski
@chimp1nski Жыл бұрын
But I see you are starting to master the dark arts of controversial max-user-engagement content creation. Well done Anakin.
@nekomatamune
@nekomatamune Жыл бұрын
In the context of this video, the benefit of the return type is to guard against the case when the so-call "truth" is incorrect (i.e. you have a bug in the function or whatever other functions it calls)
@yt-sh
@yt-sh Жыл бұрын
exactly, it helps in this case
@peter8261
@peter8261 Жыл бұрын
Prime's mustache is going to get some crisp competition from Theo at this rate.
@Kirkland_Signature
@Kirkland_Signature Жыл бұрын
Not sure I get the reasoning behind the second example, that’s exactly what return types are for. If you’d specified a return type of User you’d immediately see that password is not a valid property of that type. And if that should be a valid property why not just add an optional property to the type itself? It’s like the example assumes that this one function is the only place where that type would ever be used.
@lkjhoiuy97yjhgghfyrthgvjhguty
@lkjhoiuy97yjhgghfyrthgvjhguty Жыл бұрын
2. TS is not lying. TS has a structural type system, so it willl look at the return and see that “yeah this object has at least the things User has”. A better way would have been to write const user: User Then TS would yell when you try to add another key.
@moromann1
@moromann1 Жыл бұрын
These are quite intricate examples though that mostly make explicit returns look bad because TypeScript sucks. In most cases, explicit return types let you catch mistakes earlier. If I have a function which should return a string and I forgot to toString a number, an explicit return type will let me know right then and there that I made a mistake and I’ll fix it right away. With inferred return types this error may be discovered a lot later, perhaps by another developer, and more time will be spent debugging. Either way, I think we can all agree that these past few days of discussion have taught us something we can all agree with: a type system that doesn’t actually enforce its types is dumb.
@jordondax
@jordondax Жыл бұрын
I don't understand comments like this. Typescript is what we have right now as it is. If we want it changed we need to open a PR and make that change. When we come to a conversation we must remain within the bounds of reality. If Return types can blow your foot off, then it's strictly bad advice to be dogmatic about it. There are tradeoffs and it's good to always remember that. Typescript sucking has little to do with the convo, because that's irrelevant to the issue at hand. The issue was saying one way or the highway.
@uchechukwuokereke8497
@uchechukwuokereke8497 Жыл бұрын
I have seen this error in production. The function was meant to return a string, but someone forgot to call the "toString()" method after "parseFloat()".
@pgrzybek
@pgrzybek Жыл бұрын
I agree that inferring is usually better as it's THE truth about the function, but I also use function declaration as contracts (as in the interface). It defines what is the function for and what is expected of it. In such case I don't want the type to be inferred because I want to detect if I'm accidentally returning something I wasn't intended to return (like "string | number" instead of just "string").
@echobucket
@echobucket Жыл бұрын
Right but in the password example the contract is that it should return a "User"... and type script just ignores the fact that you stuck "password" in there.
@pgrzybek
@pgrzybek Жыл бұрын
​@@echobucket I find this "password example" a bad one. When your function returns an object that fulfills the User interface it's a contract that you can use it whenever you need a user. It doesn't really matter if it's an AmazonUser or FacebookUser. In OOP you could compare it to polymorphism, though here it's better as no inheritance is required.
@Mitsunee_
@Mitsunee_ Жыл бұрын
the problem with overloads is that inside the function bodies all typechecks run against the implementation signature and IGNORES the overload signatures entirely. Literally everytime I try to use overloads I end up with the output typed the way I want but having to lie a bunch of times in the function implementation to the point where I'm no longer confident in my code, delete it all and figure out a way to use generics instead or just do something else entirely.
@joshhoover1202
@joshhoover1202 Жыл бұрын
Pretty early on when I was learning typescript I realized this problem existed and because of it I always preferred inferences. What I have been doing (idk if it is a best practice or not) is, I will write up the function declaration often with a return type, but once I am done with writing the function I go back and delete the return type and then mouse over the function to see what the inferred type is and do a bit of a sanity check checking if it matches my explicit typing and if not, why is it different. I then leave it as is with just the type inference.
@ordazgustavo
@ordazgustavo Жыл бұрын
So, the problem is TS, not the return types
@PauloMiello
@PauloMiello Жыл бұрын
ok going into the video I was 100% sure I was going to disagree, because return types are the way to go, period. HOWEVER I didn’t know just how broken typescript type system is; I learnt it watching this. So in this particular case you are right, don’t use them. But it is only because typescript is broken: in any other language that is not, please do
@morningstarsci
@morningstarsci Жыл бұрын
how is this any different than in other OO languages where if you cast to a super type you lose the subtypes data?
@PwrXenon
@PwrXenon Жыл бұрын
Explicit > Implicit, its the reason we switched to typescript because of javascript's looseness.
@bustamantedev
@bustamantedev Жыл бұрын
I think of Return Types like a contract for the function consumers of what to expect from there, which in my opinion is the better approach. The first two examples you're giving are because you're concerned about (1) narrowing the type values and (2) missing attribute in the type definition; those 2 scenarios sound like an incorrect/incomplete type definition rather than an issue with the return type approach
@t3dotgg
@t3dotgg Жыл бұрын
The problem is that the "correct" definition is overridden by the return type. It is TRIVIAL to cast over the truth with lies. "But if your code was correct it would be fine" isn't a real argument.
@bustamantedev
@bustamantedev Жыл бұрын
​@@t3dotgg You are right, the definition is indeed overriden. However, the return value still complies with the expected return type, it is not like you can just return anything you want. To be fair to your point of view, I too rely much more on inference than explicit return types but I just don't think those arguments are as solid as you think they are. Great video, keep it up 👍
@thanhn2001
@thanhn2001 Жыл бұрын
If you didn't want the return type to lie, shouldn't you just declare a return type the tells the truth. Having said that, I really like the idea of not needing to define a return type and let inference do the work. I'm fairly new to TS so I wasn't aware of any of these dangers. I learned a lot. Thanks for the great content.
@benjamin_fdw
@benjamin_fdw Жыл бұрын
I was a C++ dev when I learn function overloading, I was still a C++ dev when I learned it was a bad idea... I was doing python when I learned that I can return different types with the same function, it instantly felt like a bad idea... Now I am doing typescript, functions get either one or two parameters (most of the time an option object with a bunch of ? fields and it returns a single type of value or at worst a type | undefined. In some very rare occasion it may be useful but most of the time you are trying to provide different answer for the same question. I am no phylosopher, I can only provide a buggy but consistent truth to each question.
@MrJester831
@MrJester831 Жыл бұрын
Omg Theo just learn Rust already. This video just makes TS look bad
@zzzyyyxxx
@zzzyyyxxx Жыл бұрын
For real, languages that have sound null safety simply _would not allow you_ to return that password example.
@HHJoshHH
@HHJoshHH 8 ай бұрын
Super bad ass video concept to have all those Engineers give their opinion at the end of the video. That style of editing? video design? Idk but very cool.
@Krzysiekoy
@Krzysiekoy Жыл бұрын
This whole "I'm gonna say my last word and then "end the convo" feels kinda shitty. Not gonna lie.
@ДавидПлеван
@ДавидПлеван Жыл бұрын
True, the "I just want to end the convo" is just a passive-agressive dance-around way of admitting defeat and a lack of any based arguments.
@joeellul-turner1280
@joeellul-turner1280 Жыл бұрын
Can you do a breakdown of 'type' Vs 'interface'? Might be a silly question but I don't get the difference 😂
@CottidaeSEA
@CottidaeSEA Жыл бұрын
This is a bit of a simplification, but a type is a structure, an interface is a functionality schema.
@Elias-vs2dx
@Elias-vs2dx Жыл бұрын
Easy solution just use plain JavaScript
@wlockuz4467
@wlockuz4467 Жыл бұрын
Preach
@christopherpoulsenfernande1624
@christopherpoulsenfernande1624 Жыл бұрын
A really good argument of why not to use explicit return types unless it's absolutely necessary. I just saw Prime's video on this topic and I have to say I find yoir argument more thorough and compelling.
@lkjhoiuy97yjhgghfyrthgvjhguty
@lkjhoiuy97yjhgghfyrthgvjhguty Жыл бұрын
3. I would say that you lied to TS, you told it what you returned and then pulled a fast one on it. This is something that a test will find.
@xtinctspecies
@xtinctspecies Жыл бұрын
Anonymous types is what you get by inference.. some people including myself.. hate it.
@JLarky
@JLarky Жыл бұрын
It's actually possible to test if return types do what you think they do... With unit tests 😱 and tools like dtslint and tsd can help as well
@goblinlordx6108
@goblinlordx6108 2 ай бұрын
Just curious... Why not just return a concrete class instead with read only public properties? This way you get nice concise types which don't leak properties which aren't intended to exist?
@alexodan
@alexodan Жыл бұрын
that ending was awesome, keep it up Theo always great content!
@bryson2662
@bryson2662 Жыл бұрын
Prime is right
@n4bb12
@n4bb12 Жыл бұрын
In the user examle, with and without return types, you don't get completion for defining the user. And if you use a return type and a property is missing, the error is on the return statement, not in the place where it's missing. What I'd recommend instead: If you want the user to conform to the type, declare it as `const user: User = { ... }` and no return type. You'll get completion for the properties. If a property is misspelled, you'll get an error directly on the wrong line. Similarly, if you return the result of an `Array.map`, pass the type to the map call instead of using a return type: `return array.map(item => ...)`. This gives you an error on the correct line instead of on the whole return. If you don't need the user to conform to the type, just return the object, and no return type. Either way, you don't need a return type. Just use types in the locations where you want errors to show up, preferably as deep down as possible.
@loucyx
@loucyx Жыл бұрын
Thanks for doing this, Theo! Folks coming from languages like Java expect to use JavaScript the same way they used Java through TypeScript instead of learning to use TypeScript. TypeScript is the best when you type when it can't infer the type, so it is actually "JavaScript with Types" instead of "Typed JavaScript," which is VERY different. Not to mention that TS is there to help with DX, not to validate your implementation. Types are closer to being a linter than to being a validation or a test, as some folks think they are. The original video by "ThePrimeagen" had some glaring issues, mainly when you try to use his functions and get very bad DX (no autocompletion) compared to relying on inference which had a great DX. Again, thanks for doing this! Again, thanks for doing this!
@ParkourGrip
@ParkourGrip Жыл бұрын
I have to disagree with this video. 1) In the first example you can easily use explicit return type Result. A developer can also put a explicit return type Result if he want's to tell the caller of the function not to rely on the implementation detail that the string is always going to be "yay". That's because in the future the implementation might change and some other string could be returned. When that change happens all the code that used this function and that did not rely on this string to be exactly "yay" will not brake. 2) The 2nd example is the only valid problem in this video. However implicit return types still do not solve the problem that is stated. Even if the return type of the "getUserInferred" includes the "password" property, whenever you pass that return value to a function "sendUser" that expects a user without a password as a argument, TypeScript would still show no errors and the password would still get leaked. 3) The 3rd example does not argue that explicit return types are bad. It argues that TypeScript function overloadings are bad. You can still get the same vaigue return type by using explicit return types without function overloadings. The moral of this example is to use function overloadings only when the benefits outweigh the massive drawbacks, and when that is the case, developers should be extra careful. It's just like using unsafe blocks in Rust.
@dahlton8310
@dahlton8310 Жыл бұрын
I’m confused, wouldn’t you want it to error out when you’re adding a field to a type that doesn’t exist on that type? Since you never said it exists on the type? I don’t work with ts too much
@witchmorrow
@witchmorrow 9 ай бұрын
Can someone answer this for me? The industry standard for typing at boundaries seems to be to use zod library. Yet when you make a schema in zod, my understanding is that (unless you use `strictObject`), it will allow any extra properties that you haven't listed to be present, but it will 'cut them off' ie 'be lying' in the way that Theo has a problem with, ie if you try to access that property elsewhere, it will error. So if all these prominent people agree with Theo about using inferred const types instead, how does zod fit into that?
@ZantierTasa
@ZantierTasa Жыл бұрын
The second example seems to be a misunderstanding. "type" in typescript is really the same as "interface". There is no lying going on. The third example.... what even is that code? Giving 3 overlapping type signatures with 1 function body? Maybe it is lying. Maybe it shouldn't even be allowed as valid typescript.
@theondono
@theondono Жыл бұрын
This is a very weird argument. You’re telling us a feature is bad just because Typescripts implementation is bonkers. I feel it’s like saying an algorithm is bad because my implementation works badly.
@Oskar1000
@Oskar1000 Жыл бұрын
It would be weird to keep using a broke feature no? If return types don't work well in ts in some instances, avoid them then. That's the reason I avoid enums in ts. The implementation is weird.
@theondono
@theondono Жыл бұрын
@@Oskar1000 the thing here is that there’s a difference in perspective between someone who uses Typescript (like maybe you and me) and someone like Theo who is a “public figure” in the ts community and can influence stuff. As someone with a platform I think it would be better to try to push ts to be a more coherent and productive language, rather than teaching users to avoid the broken parts. The latter creates more problems than it solves, for instance, next time you’re debating if ts is worth it over js, your argument is now that types are great, but only most of the time, and sometimes they suck and you need to avoid them. 😂
@Oskar1000
@Oskar1000 Жыл бұрын
@@theondono I don't actually mind the as const solution. It's quite neat most of the time. Sometimes I use return types if I have a need for it. To me it makes sense to say. Hey don't use this feature. It is broken. Hope they fix it. I think Theo would agree that they should fix that behaviour with the return types. It doesn't work as you would assume it does.
@MaxPicAxe
@MaxPicAxe Жыл бұрын
Notice that the type of a deterministic function that is being passed with compile-time constants is the result of the function itself. I believe that we should still specify return-types if an API is open to changing, that is, we expose a certain interface that can have different implementations and we want the end-user to depend on this slightly-more vague interface rather than the specific implementation outcome.
@AlecMaly
@AlecMaly Жыл бұрын
Great content, thank you!
@Meow_YT
@Meow_YT Жыл бұрын
I mostly infer types, unless I'm not sure what I'm doing when writing the function and still deciding how it's going to work. If I know what I'm expecting to return, I'll type it, just to make sure I don't lose track and make sure the errors the IDE gives me remind me. Nya
@aleksandr2245
@aleksandr2245 Жыл бұрын
what about const Generics that will be introduced in 5.0? Won't they fix it?
@PaulSebastianM
@PaulSebastianM 10 ай бұрын
Let's not beat around the bush here. This an inherent design flaw of TypeScript and I say this with a heavy heart as I truly enjoy writing TypeScript. I just wish it didn't have so many traps and footguns for juniors which can lead to buggy applications.
@romanstingler435
@romanstingler435 Жыл бұрын
The more I watch this, the less likely it is that TS is something I would want to see ever.
@Guilherme-qk9so
@Guilherme-qk9so Жыл бұрын
Isn't the fact that TS doesn't complain about the overload example you mentioned a good reason NOT to use overloads?
@paulholsters7932
@paulholsters7932 Жыл бұрын
I am learning TypeScript since when I write TypeScript it is obviously wrong sometimes. And not only because of the returntypes. It is extremely easy to write unsafe code in TypeScript. So it’s time to start mastering it. And hopefully I discover how to write safe code with it that is acceptable for large and complex backend projects. Because with how I write it now it sure as hell is not. Right now I feel like Typescript gives you nothing but an illusion of safety.
@bravelyjake
@bravelyjake Жыл бұрын
Do you think that final problem(user/admin) could possibly be solved by a library, similar to what Zod does with enums, or ts-pattern does with its exhaustiveness checking?
@Imjoshnewton
@Imjoshnewton Жыл бұрын
This is why you have to override the user type in create-t3-app to access things on the user table without compiler errors.
@eduardosanzb
@eduardosanzb Жыл бұрын
Your proposal would not be amazing in a huge codebase
@MrChickenpoulet
@MrChickenpoulet Жыл бұрын
8:50 i'm not sure this example is great to advo:ate against return type, as you mentioned we get back a "vague" type whereas with the specific return types we can be wrong (in the case you showed). BUT, if we type the `getUserInferred` to return a `{ role: User } | null`, we get a better return type, and it's correct: ```ts type User = "admin" | "user" const getUserReturnType = (role: User): {role: User} | null => { if (role === 'admin') { return { role: 'admin' } } if (role === 'user') { return { role: 'user' } } return null } const result = getUserReturnType("admin") // result is { role: User } | null, obviously, and there are no lies ```
@ThomasWSmith-wm5xn
@ThomasWSmith-wm5xn 9 ай бұрын
Primes video - why you want return types XD
@TomasVaisar
@TomasVaisar Жыл бұрын
I don't know, man. Inferred types are maybe cool with simple examples like these, but they are pain to debug once you have two-three nested objects. I mean, sometimes VS Code cannot even display the entire error and you have to install extension that moves the actual error to the top of the error message 🤦‍♂ (yes, I know you can run the compiler manually and see the full error in the console)
@PileOPoop1
@PileOPoop1 Жыл бұрын
At least I know I can never work with someone who actually believes a contract is a lie.
@Blast-Forward
@Blast-Forward Жыл бұрын
4:55 Structural typing vs nominal typing. 💀
@legotechnic27
@legotechnic27 Жыл бұрын
Imo return type is important to declare and document intent of the function to some degree. It also helps preventing mistakes in the implementation of the function, and even provide autocomplete/intellisense while typing return values. What would be interesting tho, to also address your concerns with having return types, is if you could specify `myFunc(...) satisfies someType {...}`. This would work similar to how the satisfies operator already exists in TS now, but applied to type checking of return values without overriding the inferred type of the function.
@samuelgunter
@samuelgunter Жыл бұрын
getting ready to eat that prime steak
@larryd9577
@larryd9577 Жыл бұрын
The User type in the example is not lying it is just named wrong, it is a view to the user without its password. And you should not call .password on in, nor should it store the password in js-world to it.
@harry_jms
@harry_jms Жыл бұрын
How can opinion be wrong?
@valtism
@valtism Жыл бұрын
Hoooly damn that celeb reel at the end!
@clingyking2774
@clingyking2774 Жыл бұрын
As a noob, I'm confused.
@archip8021
@archip8021 Жыл бұрын
This demonstrates a big weakness of typescript
@danvilela
@danvilela Жыл бұрын
Hey Theo, make a video about satisfies keyword in TS! Also, maybe you should grow a goatee, might be cooler than the stash
@artyhedgehog
@artyhedgehog Жыл бұрын
Now you're getting to the point where you're so right I almost hate you for that.
@vitiok78
@vitiok78 Жыл бұрын
The main problem is that the whole Typescript is a lie. Typescript is an advanced documentation tool and a linter around that good old JavaScript. Nothing more.
@imjens.k
@imjens.k Жыл бұрын
Does anyone else think using const inference doesnt actually matter if you design your system's types correctly?
@dpaulflavius
@dpaulflavius Жыл бұрын
Basically you're saying that if you buy a boat, you can't drive it in the sky...man..
@donnyverduijn6605
@donnyverduijn6605 Жыл бұрын
At 5:18, you say it's in an incorrect type definition, but it is not, since TypeScript has a structural type system and not a nominal type system.
@1DJRikkiBee
@1DJRikkiBee 7 ай бұрын
So, we can't use return types anymore because you don't understand structural typing and some people don't proofread? That seems like a weak reason, especially since "as const" doesn't work on more complex things like computed values.
@anwarsjy
@anwarsjy Жыл бұрын
Nicely done
@magne6049
@magne6049 Жыл бұрын
6:29 nitpick, but I think the return type should be drawn as a strict subset of the truth here.
@Vim_Tim
@Vim_Tim Жыл бұрын
You are right. This is not a nitpick: this is exactly what distinguishes a “lie” from desirable behavior (encapsulation).
@simonhartley9158
@simonhartley9158 Жыл бұрын
I think that I've been convinced. While classic types can help as enforced documentation, in a structurally typed language, it's easy for the type to become complicated enough for a human to get wrong.
@complexlity
@complexlity Жыл бұрын
First time hearing maple speak. Inference wins (for now)
@rexforde
@rexforde Жыл бұрын
Is there a satisfies for return types? That could be a middle ground for this debate.
Жыл бұрын
In this example you defined the return type incorrextly so the result is incorrect. To know what the result type based on condition you can use conditional types. I think this debate is much like TS vs JS. TS is the "explicit" end of the spectrum and we all know where this discussion has gone.
@soniablanche5672
@soniablanche5672 Жыл бұрын
I only type the return when the inferred type is "any" due to Typescript limitations
@danieleruffini1053
@danieleruffini1053 Жыл бұрын
In first example as const should have precedence in intellisense. The others are straight up bugs to me
@MarcelRobitaille
@MarcelRobitaille Жыл бұрын
I would expect a good type system to perform static analysis and give me a diagnostic when I do `if (role === "user") return { role: "admin" };` and when adding password to User. I think functions should have explicit return types, but that return type should be enforced inside the function. It's crazy that typescript goes to so much effort to bolt types onto javascript then lets you lie so easily and subtly.
@josephizang6187
@josephizang6187 Жыл бұрын
Thanks Theo
@nahfamimgood
@nahfamimgood Жыл бұрын
dig the mustache.
@benjaminkinga7797
@benjaminkinga7797 Жыл бұрын
I hate when people I dislike are correct ):
@asdqwe4427
@asdqwe4427 Жыл бұрын
If you have a legit case to return one of the following strings "one", "two", "three". Why not just type that and name it something that hints at why it exists? Just using type inference to figure out what is going on make me less likely to think that the developer actually had a reason for not using "string". I think the code in your examples do a pretty good job of communication, but they could 100% be typed as well.
@DrewLytle
@DrewLytle Жыл бұрын
I just love Kent C Dodds 😂
My browser got hacked and it cost me $2,000
21:40
Theo - t3․gg
Рет қаралды 57 М.
Infer is easier than you think
13:38
Matt Pocock
Рет қаралды 90 М.
🍉😋 #shorts
00:24
Денис Кукояка
Рет қаралды 3,4 МЛН
ПРИКОЛЫ НАД БРАТОМ #shorts
00:23
Паша Осадчий
Рет қаралды 6 МЛН
Bike Vs Tricycle Fast Challenge
00:43
Russo
Рет қаралды 98 МЛН
Why use Type and not Interface in TypeScript
14:12
ByteGrad
Рет қаралды 207 М.
How NextJS REALLY Works
28:25
Theo - t3․gg
Рет қаралды 148 М.
TypeScript Slows You Down - Here’s Why
5:44
Theo - t3․gg
Рет қаралды 39 М.
How Did I Not Know This TypeScript Trick Earlier??!
9:11
Josh tried coding
Рет қаралды 212 М.
Dear Oracle, it's time to free JavaScript
24:25
Theo - t3․gg
Рет қаралды 75 М.
Is Computer Science still worth it?
20:08
NeetCodeIO
Рет қаралды 329 М.
I WISH I Knew These Tailwind Tips Earlier
9:15
Theo - t3․gg
Рет қаралды 188 М.
Type your functions in TypeScript and SAVE TIME
8:31
ThePrimeTime
Рет қаралды 51 М.
I Cannot Believe TypeScript Recommends You Do This!
7:45
Web Dev Simplified
Рет қаралды 172 М.