,

Use Caution When Defining Return Types in TypeScript

Posted by

Be Careful With Return Types In TypeScript

Be Careful With Return Types In TypeScript

When working with TypeScript, it’s important to be mindful of return types when defining functions. TypeScript is a statically typed superset of JavaScript that adds type checking capabilities to the language. This can be very useful for catching errors early on in the development process, but it also means that extra care needs to be taken when defining function return types.

One common mistake that developers make when working with TypeScript is not specifying a return type for a function. This can lead to unexpected behavior and make it difficult to understand the function’s intended usage. It’s important to always explicitly define the return type of a function to ensure clarity and consistency in the codebase.

Another common mistake is defining an incorrect return type for a function. For example, if a function is intended to return a number but the return type is mistakenly defined as a string, it can lead to runtime errors and unexpected behavior. It’s important to carefully consider the expected return type of a function and ensure that it accurately reflects the actual return value.

Additionally, generic return types can be useful in certain scenarios, but they can also introduce complexity and make the code harder to understand. It’s important to strike a balance between using generic return types for flexibility and maintaining readability and maintainability in the codebase.

In summary, when working with TypeScript, it’s important to be careful with return types to ensure clarity, consistency, and correctness in the codebase. Always explicitly define the return type of a function, ensure that it accurately reflects the actual return value, and carefully consider the use of generic return types to strike a balance between flexibility and maintainability.

0 0 votes
Article Rating
27 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
@t3dotgg
9 months ago

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 ❤

@witchmorrow
9 months ago

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?

@PaulSebastianManole
9 months ago

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.

@edantas
9 months ago

Me, who doesn't want to use javascript, before watching this video: Nice, TS allows me to code safely with type safety.
Me, after watching this video: fuck this, lets Go or Rust

@artist6000ish
9 months ago

@ 8:38 "and a code review is the only way you're not going to ship this code"

Spoken by someone who doesn't believe in automated testing. Yeah buddy. If you're relying on code-reviews to catch bugs of this type, you got bigger problems.

@Ultrajuiced
9 months ago

4:55 Structural typing vs nominal typing. 💀

@leakyabstraction
9 months ago

I think a lot of comments reflect that typical attitude when someone doesn't want to understand the point. I'm coming from a C#/.NET backend developer background, and I wrote out return types for a long time, but since I started to properly use and understand the Typescript type system, I realized that it's often harmful to do so, and one of the prime reasons is that the inferred return type actually tends to be more precise. While a naive developer would often write "number" or "string" as the return type, the inferred type is narrower than that when TypeScript sees that only certain constant values can be returned.

And yes, it's possible to maintain a precise return type, but what's the point when TypeScript does that for free. It's just useless clutter usually. It reminds me of cases when people waste time creating inheritance hierarchies in TypeScript while union-based typing would be more appropriate, would give you what you need for free, in an always precise and up-to-date manner, with all the benefits of type narrowing.

Of course it ultimate boils down to the intent: In cases where you know exactly what do you must return, and you explicitly want to generalize or constrain the contract irrespective of the real type, then you should use explicit return type. But those are the minority of cases in my opinion, yet some developers (and to be honest, mostly the ones with legacy background/thinking) religiously do explicit return typing, to the point of writing things like "flag: boolean = true".

I mean let's not kid ourselves, plenty of TypeScript users are not experienced TypeScript devs, and they think that if something has no explicit type then it's "any", and type safety is not used. And the same people tend not to don't understand literal types. TypeScript provides abundant opportunities to write pragmatic, streamlined, modern code, and so many waste this opportunity but adding useless complexity and clutter to things.

@christopherpoulsenfernande1624
9 months ago

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.

@livingfreely
9 months ago

You should use return types. It sucks that TS is broken like that.

@TypeScriptTV
9 months ago

Explicitly annotating return types is necessary when dealing with recursive functions: https://www.youtube.com/watch?v=nVd8jvjK4Y0&t=127s

@jonnyso1
9 months ago

Nah, that's just typescript beeing horrible.

@user-hk3ej4hk7m
9 months ago

You could also just do Result<"yay">. It'd be great to have an option to specify covariance and contravariance of return types types in TS, as it looks like it should solve most of these problems. In the case of typescript not being that strict with types I most of the time remove the return type, check the inferred type and then add the type back.

@NuncNuncNuncNunc
9 months ago

There's a bit of a lie here. @2:00 adding the return type Result<string> is not overriding the truth. It is telling you what the truth is. If that is not the return type you want, don't define it as it is here. If "yay" is the only valid "ok" value you want, then clearly Result should not take a generic parameter. Getting rid of the generic and making all the attributes readonly is the "truth" you can define Result as to match the "truth" you want.

Not sure there is any solution to Prime's example other than to recognize that overriding a function with different return types is inherently dangerous and overriding with a less specific return type is probably never ok.

@Danielo515
9 months ago

O yeah, let’s keep more arbitrary rules in our minds while programming rather than accepting that TS completely sucks

@mpiorowski
9 months ago

Isn't the first example just a case of wrong type argument? You supplied it with string, and this will work perfectly if You would support it with "yay". This is a shitty limitation of typescript, that he allows this..

@neilmm
9 months ago

In the example of returning an ok | error result, what if you wanted to use a function to create the ok result or the error result and you also wanted the RT to be Result<A, B> for readability instead instead of the union of two objects?

@complexlity
9 months ago

First time hearing maple speak. Inference wins (for now)

@Salehalanazi-7
9 months ago

What kind of bullshit is this. the code you show is your own bug. instead of having clear barriers you would rather have truthy code?

@adaliszk
9 months ago

Interesting. I wonder though what is the reason to have super accurate representation of your data instead of its type? Like, once you try to tell typescript about how your data is mapped in various scenarios, it becomes less accurate and even lies as you have shown. I personally prefer using return types for enforcing a contract on my functions with a type, but without assuming what the data is, since that makes it easier to extend while maintaining the exact interface that the rest of the codebase expects. While inferred types does the same check within the codebase level, it does not block developers changing the interface itself if the stars align up right and they narrow down the use-case well enough that only one portion of the codebase is affected.