When it comes to TypeScript, a big part of the game is defining types.
With this, we can define annotations, but they can appear in more places.
...
For further actions, you may consider blocking this person and/or reporting abuse
I'm fully aware that inferred function return types are considered idiomatic in TypeScript. In the end though I think that simply reflects an attitude where convenience wins over type safety (i.e. the popularity of TypeScript + VS Code is much less about type safety but much more about convenience).
Google TypeScript Style Guide: Return types
Whether to include return type annotations for functions and methods is up to the code author. Reviewers may ask for annotations to clarify complex return types that are hard to understand. Projects may have a local policy to always require return types, but this is not a general TypeScript style requirement.
There are two benefits to explicitly typing out the implicit return values of functions and methods:
One minor point is the fact that functions have a type. One of the more infuriating things is that you cannot type a function declaration with a function type - one has to use arrow functions for that. So by not explicitly specifying the return type half of the function's type is left unspecified/implicit. Sure, an IDE will be able to show that type but that means that now even code reviews have to be conducted in the presence of "assistive technologies" (as the explicit code simply doesn't tell the whole story).
The major point however:
A Gentle Introduction to haskell Version 98 - 2.2.1 Recursive Types
"For example, suppose we wish to define a function
fringe
that returns a list of all the elements in the leaves of a tree from left to right. It's usually helpful to write down the type of new functions first"The return type of a function is part of its design, its contract so it makes sense to explicitly determine and state it even before the body is implemented. So if one is already thinking in types and is perhaps even practicing type driven development then it makes sense to explicitly capture the types in play, perhaps even adding "more boilerplate" by adding type aliases that give the types more descriptive and meaningful names.
An implicit return type projects an attitude that the return type is merely an accidental byproduct of the function's implementation - while types are mandatory for the inputs (arguments);
It's one thing to rely on type inference within the confines of a function body - but crossing the function (or method) boundary is significant enough to warrant an explicit type check/lint (even for one liners) in order to catch problems as close to the source as possible.
Does that mean more typing on the keyboard? Sure; not enough though to affect productivity.
Interestingly Effective Typescript has "Item 19: Avoid Cluttering Your Code with Inferable Types" (p.81) which unsurprisingly states:
"The explicit type annotation is redundant. Writing it just adds noise. If you’re unsure of the type, you can check it in your editor."
Only to later clarify (p.85):
"Similar considerations apply to a function’s return type. You may still want to annotate this even when it can be inferred to ensure that implementation errors don’t leak out into uses of the function."
The item is summarized (p.87) with:
You don't need to go into an advanced discussion to see why return type annotation is useful. What if it's 7am, you haven't got your coffee yet and you make a very simple typo as a result?
Return type annotation will catch it, isn't it?
So again the return type is a consequence of the implementation.
When you "think in/develop with types" the implementation is a consequence of the desired type - i.e. the relationship it flipped.
Consider this scenario
We find that
MyType
also needstotal
.No error that the result from
fn
is no longer sufficient. If we are lucky somewhere the result will be delivered to an explicitly typed binding, alerting us to the problem with the function (or any others that produce a similar result).With an explicit return type compilation will identify all the functions that no longer produce the correct type.
i.e. the type is changed before the implementation(s).
A capability in terms of value-orientation:
i.e. contracts (constraints) first, implementation last.
Aside Why type-first development matters
My intent was to simulate a defect (which perhaps would only be detected at an explicitly typed site of use), i.e. the intended return type should either have been
string
ornumber
- notstring | number
.JS Doc TS with (hand written) declaration files is "Typed JS". There's a clean separation between "type space" (TypeScript types) and "value space" (the JS implementation).
The entire point of the
index.d.ts
andinternal.d.ts
files is to explicitly formulate a constrained set of types that are expected to be encountered (without having to formulate them "ad hoc").The problem is that using JS Doc TS well (as far as I can tell) requires a higher level of TS competence (especially its "typing language") than writing TS destined for transpilation.
Those features can help to cut down on verbosity and emerge as TS tries to tease whatever type information it can out of the JS value space to make it available in type space. But in terms of design, types are typically formulated before the implementations and defined in type space (rather than extracted from value space).
Type space derived from value space is like drafting a blueprint after the house has already been built.
Design by Contract is attributed to Bertrand Meyer in connection with the Eiffel OOPL (1986) but the "type first" notion appears elsewhere like in Clojure's "thinking in data, modeling in data, and working in data" which is most definitely not OO.
And the idea of "function types as contracts" is up-to-date - 3.7.1 Types and Contracts.
Explicit types serve as deliberate "check points" during type checking/linting.
Expedience is always fielded as the primary motivation for "implementation first, contract last" approaches. But more often than not, contract last approaches save you time in the short term but create problems (that could be avoided by actively managing your types) in the long term (TTIS - Time-To-Initial-Success).
To some degree it's like saying "I know that JSDoc annotations are helpful for maintenance but keeping them in sync with the code slows me down so it's not worth it". Types are part of the application's specification that are supposed make it easier to reason about the code, so it helps if they are not buried somewhere in the implementation.
I guess the lesson here is: just because TypeScript is being used doesn't actually reveal whether the potential of types is being leveraged to maximum effect (to the extend that is even possible in TypeScript).
Aside:
declare
)Slightly erroneous wording: types are inferred, not derived. Also
readonly
properties are missing here, even if you only want to show basic types and leave generics and advanced types for later.Fully aware and thanks for raising this.
However in the big scheme of showing these basics to beginners in TypeScript I think it's a valid explanation.
We don't always want to make things the neatest when explaining certain topics.
For instance your example with return values is super valid, but if we don't explain the options how will people know about it, when they do need it?
Besides I did note in the article you don't need to explicitly name variables that are assigned already.
Anyway, love the debate going on here, but please keep in mind these articles are to show a very basic starting point to TypeScript.
What about this place ;)
Exactly, but isn't this fact making situation worse?
Love this point!
I split up my TypeScript into several articles, so might just focus one whole article on this point
Return type is part of the context, preserving it helps reading the code.
It's sad that you consider valid only your taste. Worse is that you telling me what I should do according to your inability to accept approaches different from yours. Just keep in mind that not everyone using your favourite IDE.
You're still missing the point: there are essential tools and there are convenience tools. Version control, build tools and compiler, etc. are essential, you can't build software without them. IDE is a convenience tool. One can change such tools without affecting ability to build software. You prefer VSCode, somebody else prefers vim or some other IDE. It's not your decision what is productive to them.
Again, I see no point to argue and don't understand why you're trying to convince me that your taste is better than others.
When you write code, you have a local context - business logic, algorithm, variables/parameters and their types. If you need to call some function, there is a narrower local context too - what you need to pass as parameters and what you'll get back. If you only writing code, often you need to take a look at function declaration to figure out what needs to be passed and what it will return. If return type is inferred, you have to look inside the function code to figure out the return type. The same happens when one reads the code (especially outside the IDE). Need to look inside the code to figure out something is a clear sign that part of context is lost. And lost context is a source of distraction and increased mental overhead. Brevity is good as long as it doesn't loss context.
I see no point to argue. I prefer to be explicit, preserve context and use language capabilities to express my intents. I believe that this is important, because my projects usually living many years and often it's not me who reads and changes my code and I can't make any assumptions in what conditions it will happen.
You prefer to rely on external, not essential and not always available tools and assume that you or someone, who will be reading your code, will have the same or similar non-essential tools. I see no problems with that. Your project, your code, your rules. I'm just trying to point that conditions could be different and your approach is not always applicable.
You, seems, read my answer not carefully enough. There is often a need to read code not only in IDE.