

How to Measure Your Type Coverage
source link: https://tomasvotruba.com/blog/how-to-measure-your-type-coverage/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How to Measure Your Type Coverage
2022-11-07
When we come to a new code base, we look for a code quality metric that will tell us how healthy the code base is. We can have CI tools like PHPStan and PHPUnit. PHPStan reports missing or invalid types, and PHPUnit reports failing tests.
But how do we know if 10 passing or 100 passing tests is enough? What if there are over 10 000 cases we should test?
That's where test coverage gives us a hint. Which project would you join if you could pick: the one with 20 % test coverage or the one with 80 % test coverage? I'd always go with the latter, as tests give great confidence.
Yet, tests are not the only thing that can help us access code quality quickly. With PHP 7.0, 7.4, and 8.0, type declarations became a sign of project health. But how can we measure those?
Do you measure your type declaration completeness with @phpstan already?
— Tomas Votruba 🇺🇦 (@VotrubaT) October 26, 2022
You should 😉
It's such a great and safe feeling to see 99 % type-coverage 😎 pic.twitter.com/cYyDVYKqG8
What is the "Type Coverage"?
If the test coverage is % of all possible code runs, what is the "type coverage" then?
function run($name)
{
return $name;
}
Here we have 1 param and 1 function return. That's 2 possible type declarations that we're missing:
- 0/2 = 0 % type coverage
How can we increase it? We add a type declaration into the function param:
function run(string $name)
{
return $name;
}
Here we have 1 param with type declaration, and 1 return without it.
- 1/2 = 50 % type coverage
How do we get to 100 %? Exactly, we add the return type declaration:
function run(string $name): string
{
return $name;
}
We do the same for typed properties as well:
private $name;
private $surname;
private $age;
- 0/3 types are completed = 0 % type coverage
What about this code?
private string|Name $name;
private ?string $surname = null;
/**
* @var callable
*/
private $addressCallable;
This code has 100 % type declaration coverage. How is that possible? Nullable, union, and callable docblock type declarations are valid and the most strict types.
Pretty simple, right?
We can run the type coverage check quickly with PHPStan on any project. Even if it's legacy or full of magic - no autoloading is needed.
I love this Metric, Because...
- it is fast - we know instantly the result
- it is simple - we know the value is between 0-100
- it is explanatory - we know the potential type is missing, and where exactly can we fix it
- it is code sustainability predictor - based on this number, we know how easy or complicated it will be to work with the codebase
3 Steps to Measure it?
The type coverage is measured by 3 custom PHPStan rules with 3 custom collectors. They work the same way as described above in the code sample.
- Install the
symplify/phpstan-rules
package
composer require symplify/phpstan-rules --dev
The package is available on PHP 7.2+, as downgraded.
- Add Rules to
phpstan.neon
The easiest type declaration to add is a return one, then the param one. On the other hand, the typed property is available as late as PHP 7.4. That's why we have 3 different rules for them, with one collector per each:
services:
-
class: Symplify\PHPStanRules\Rules\Explicit\PropertyTypeDeclarationSeaLevelRule
tags: [phpstan.rules.rule]
arguments:
minimalLevel: 0.99
-
class: Symplify\PHPStanRules\Rules\Explicit\ParamTypeDeclarationSeaLevelRule
tags: [phpstan.rules.rule]
arguments:
minimalLevel: 0.99
-
class: Symplify\PHPStanRules\Rules\Explicit\ReturnTypeDeclarationSeaLevelRule
tags: [phpstan.rules.rule]
arguments:
minimalLevel: 0.99
The minimalLevel
argument defines minimal required type coverage in every rule. Notice the value 0.99
, meaning at least 99 % type coverage is required. We'll get back to that later.
- Add Collectors to
phpstan.neon
At the moment, we've registered the rules, but they do not have any effect. We have to add collector services too:
services:
-
class: Symplify\PHPStanRules\Collector\FunctionLike\ParamTypeSeaLevelCollector
tags: [phpstan.collector]
-
class: Symplify\PHPStanRules\Collector\FunctionLike\ReturnTypeSeaLevelCollector
tags: [phpstan.collector]
-
class: Symplify\PHPStanRules\Collector\ClassLike\PropertyTypeSeaLevelCollector
tags: [phpstan.collector]
Now run to see the results:
vendor/bin/phpstan
The failed error message is more than meets the eye. It shows you where you can complete the type declarations, so you can find them in the code and improve.
How to Find Your Current Type Coverage
Now we get back to the 0.99
resp. 99 % required type coverage. The CI fails on such a high value, but that's our intention. The error message actually tells us the current type coverage value:
Out of 81 possible param types, only 60 % actually have it. Add more param types to get over 99 %
In this case, we take the current value of 60
and put it into the config, so our codebase will remain on this code coverage:
services:
-
class: Symplify\PHPStanRules\Rules\Explicit\ParamTypeDeclarationSeaLevelRule
tags: [phpstan.rules.rule]
arguments:
- minimalLevel: 0.99
+ minimalLevel: 0.60
This value can be different for param, return, and property, so adjust it accordingly to make the CI pass.
Now we re-run PHPStan, and everything is fine. We commit, open pull-request, and merge.
Lean Type Coverage Improvement
Once a week, we run the same command again, trying to bump it 2-3 % (depending on your codebase size) and open a pull request. This way, we can improve the codebase gradually, without any big bang.
We also use these rules to monitor type coverage improvement on the project we work on. That way, both developers and the client knows we're going in the right direction.
Happy coding!
Have you find this post useful? Do you want more?
Follow me on Twitter, RSS or support me on GitHub Sponsors.
Recommend
-
22
How to Measure Your Organic CTR for SERP Features [with AWR]It’s easy to imagine that a Featured Snippet inserted at the top of the SERP would influence in some way people’s decision to click, and now, thanks to recent studies, you’re also ab...
-
8
How to measure your CPU time: clock_gettime! I’m super into measuring CPU time. If you have a slow program, the first thing you want to know is whet...
-
5
-
5
Code Coverage: How to Measure You've Done Enough TestingNovember 15th 2021 new story4A program with...
-
10
epassaro Posted on Nov 19
-
11
Code coverage is a useless target measure by Mark Seemann Aiming for a particular percentage of code coverage is counter-productive. It's the end of 2015, and here I tho...
-
5
Code coverage for your AWK programs December 2022 Despite being the author of GoAWK, I don’t personally use AWK for multi-line scripts. I tend to use Python for script...
-
7
Your "Tag coverage" summary can report on up to 10,000 pages. Nicole Farley on March 8, 2023 at 12:30 pm | Readin...
-
6
Mint Mobile: How to know if there is coverage in your area
-
8
How To Pick The Best Cell Service To Get Coverage In Your Area ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK