There are many articles talking about the pros and cons of using Typescript vs Javascript, and even against other languages like C#, Java, etc. I came from Java and C# world, have seen how javascript benefited my own work.
And now I've taken the leap to use Typescript. Here in this article, I'm not going to repeat what has been talked about many many times. I'm not going to convince everybody to use Typescript as everyone has their own preference. I just want to share with everyone what that experience is like.
Improved Communication
When the amount of code in the project accumulates up, you will keep forgetting about what you have already written. People are more willing to talk in person rather than read it from the documentation as they normally don't trust it.
A typical javascript documentation would look like something as below:
/**
* Documentation Example
* @param {Object} param - Example Paramter
* @returns {Object} - Example return definition
*/
function example(param) {
...
return {}
}
It is really handy that we have tools like JSDoc that you can just grab for free to automatically convert it to nice looking documentation for you.

However, it doesn't solve all the problems as you are trying to keep and maintain two sources of truth: documentation and code. It is never guaranteed that your developers will update the documentation whenever the code changes, and it is often the case that developers will not do it and the business doesn't really care about it as it doesn't generate any value directly.
But it will come back and bite you when people start seeing the value of keeping the documentation up to date, especially when projects scale up and pressure in the following scenarios
- Working with an outsourced team or 3rd party vendors.
- Working within a team with a tight delivery schedule
- Working with another department within a big organisation

When you start to write documentation, you will realise it is very challenging to always keep documentation up to date otherwise your documentation is meaningless and your team will fall back to the workflow where multiple people focus on a single task.
As a result, how to keep documentation always true is critical.
Here typescript comes to rescue the team from chaos. An equivalent typescript version of the previous code can be written as something as following:
interface ExampleParam{
param: string
}
interface ExampleReturn{
status: string
}
function example(param: ExampleParam): ExampleReturn {
...
return {
status: "ok"
}
}
When you try to run
tsc
or use Visual Studio Code, it will throw out errors and highlight where the problems are if you try to call the functions with the wrong parameters
example("test")
// throw error as param can't be string
example({ param: 123 })
// throw error as param need to be a string
let result: string = example({param: "test"})
// throw error as the return value should be an instance of ExampleReturn
In this case, typescript acts as documentation and communication across the whole team. And the "documentation" is 100% accurate and update to date because it is generated from the code.
This really changes how people operate in a team and helps the team to iterate faster, improves the scalability of the whole team as
- The time we spent on communication can be used to finish other tasks
- Minimises the chances of misunderstanding and miscommunication

Effective Feedback Loop
Traditionally, in order to release a new product feature, no matter how small it is, we need numerous testers to do functional testing, user story testing, regression testing and then business gives feedback on whether they want to release it, improve it or abandon it.

This testing feedback loop is time-consuming and involves a lot of human effort. Only user story testing and business feedback are the parts which generate real values. In a startup organisation, it is often the case that a single tester will test everything.
Sometimes a small thing can dramatically change how a team works together. If we can improve the functional testing and regression testing, testers would be able to focus on the work that generates more value for the business.
In practice, this improvement can be achieved via the CI/CD orchestration tools such as Jenkins, Circle CI, and Docker. These tools are able to generate a test report to both business and developers. Developers don't need to wait for testers to finish their testing to continue on their development and bug fixes. Also, the test report can act as the development progress, so that the business would be able to set a realistic release date.

However, it still happens a lot that the regression and functional testing failed to catch breaking changes introduced by the developers. This result in delayed feedback loops and reduced efficiency on the team. It is especially true when we are in the process of moving from monolithic architecture to microservices architecture. Because the automated unit testing and functional testing usually tests on each individual microservice. Let's have a look at a scenario first before we dive into the details.
Tom and Steve are both developing new features to the micro-services. They push the code to the git repository, CircleCI gives a pass to all test cases and then deploy the new features. Emma has been notified that a new build will be ready for testing and she has planned her whole day for the user story testing. However, when Emma starts to do her job, she finds out some basic functionalities failed to be caught by the developers. She is blocked by the breaking changes.

So how CI/CD with typescript can improve our feedback loops?
Typescript allows different microservices to share the same interfaces. Take a blog backend as an example, it has two microservices: blog and author. For each submission of the blog, an author email needs to be passed in to reference the writer. Here we define an Author interface and a BlogSubmission interface which references the Author as below
// author/index.type.ts - author microservices
export interface Author{
authorEmail: string
}
// blog.ts - blog microservices
export inteface BlogSubmission{
author: Author
articleEmail: string
}
function submitBlog(submission: BlogSubmission){
let authorEmail = submission.author.authorEmail
....
}
However, the business introduces a new feature to provide the user with the capability to change their email addresses. It means we need to introduce a new primary key to enforce the uniqueness of each author. As a result, authorId is selected as the primary key. author/index.type.ts will be changed to
// author.ts
export interface Author{
authorId: string
}
We think this is all we need to change so we push the code to the git repository. However, it fails the test cases because authorEmail is still referred to in blog.ts while it has been removed in author microservices.
So the whole development workflow has been improved as the integration testing happens earlier than before:

Conclusion
In the end, our testers become happy as they can plan their work better. Our developers become happy as they can get notified any breaking changes quicker than before. The business and management become happy as they understand better about the progress of development and is well positioned to plan future releases.