Cyclomatic complexity is calculated based on the control flow graph of a program. Each decision point (such as if
, else
, for
, while
, switch
statements) adds to the complexity value. The formula to calculate cyclomatic complexity is:
[ V(G) = E - N + 2 ]
where (V(G)) is the cyclomatic complexity, (E) is the number of edges in the control - flow graph, and (N) is the number of nodes.
In simpler terms, you can also calculate it by counting the number of decision points in the code and adding 1. For example, a function with no decision points has a cyclomatic complexity of 1.
In TypeScript, just like in any other programming language, high cyclomatic complexity can lead to code that is hard to read and maintain. As TypeScript is often used in large - scale projects, where codebases can grow rapidly, keeping cyclomatic complexity in check becomes crucial. High - complexity functions are more error - prone and can make unit testing difficult as there are more possible execution paths to cover.
Let’s consider a simple TypeScript function:
function calculateDiscount(price: number, isMember: boolean): number {
if (isMember) {
if (price > 100) {
return price * 0.8;
} else {
return price * 0.9;
}
} else {
return price;
}
}
To calculate the cyclomatic complexity manually, we count the number of decision points. In this function, we have two if
statements. So the cyclomatic complexity is (2 + 1=3).
There are several tools available to calculate cyclomatic complexity in TypeScript projects. One popular tool is escomplex
.
First, install escomplex
globally:
npm install -g escomplex
Assume you have a TypeScript file named example.ts
. You can run the following command to calculate its cyclomatic complexity:
escomplex example.ts
escomplex
will provide detailed information about the complexity of each function in the file, including cyclomatic complexity.
Most code analysis tools can generate reports highlighting functions with high cyclomatic complexity. Once you have identified these functions, you can focus on refactoring them. For example, if you use eslint
with the complexity
rule, it can flag functions that exceed a certain cyclomatic complexity threshold.
Let’s take the previous calculateDiscount
function. We can refactor it to reduce its cyclomatic complexity:
function calculateMemberDiscount(price: number): number {
if (price > 100) {
return price * 0.8;
}
return price * 0.9;
}
function calculateDiscount(price: number, isMember: boolean): number {
if (isMember) {
return calculateMemberDiscount(price);
}
return price;
}
After refactoring, the calculateDiscount
function has a cyclomatic complexity of 2, and the calculateMemberDiscount
function has a cyclomatic complexity of 2 as well. By breaking the original function into smaller functions, we have made the code more modular and easier to understand.
Functions should have a single responsibility. If a function is trying to do too many things, it will likely have a high cyclomatic complexity. For example, instead of having a single function that validates user input, processes the data, and saves it to a database, create separate functions for each task.
Early returns can help reduce nested conditionals and thus lower cyclomatic complexity. Consider the following example:
function processUserInput(input: string): void {
if (!input) {
return;
}
// Process the input
console.log(`Processing input: ${input}`);
}
Nested conditionals can quickly increase cyclomatic complexity. Try to use logical operators to simplify conditions. For example:
function isEligible(age: number, hasLicense: boolean): boolean {
return age >= 18 && hasLicense;
}
Cyclomatic complexity is an important metric in TypeScript development. By understanding its fundamental concepts, learning how to calculate it, and following common and best practices, developers can write more maintainable, readable, and testable code. Keeping cyclomatic complexity in check is especially important in large - scale TypeScript projects, where code quality can have a significant impact on the overall success of the project.
escomplex
documentation:
https://www.npmjs.com/package/escomplexeslint
complexity
rule:
https://eslint.org/docs/rules/complexity