PHPackages                             rector/type-perfect - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. rector/type-perfect

ActivePhpstan-extension[Testing &amp; Quality](/categories/testing)

rector/type-perfect
===================

Next level type declaration checks

2.1.4(1mo ago)1035.7M↓24.5%720MITPHPPHP ^7.4|^8.0CI passing

Since May 27Pushed 2d ago4 watchersCompare

[ Source](https://github.com/rectorphp/type-perfect)[ Packagist](https://packagist.org/packages/rector/type-perfect)[ Fund](https://www.paypal.me/rectorphp)[ GitHub Sponsors](https://github.com/tomasvotruba)[ RSS](/packages/rector-type-perfect/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (4)Versions (22)Used By (20)

Type Perfect
============

[](#type-perfect)

[![Downloads](https://camo.githubusercontent.com/ad6b80aab0da96250c7aa482a922d59d6e78b4564700bf2770f1b8afc7f7cd49/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f726563746f722f747970652d706572666563742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/rector/type-perfect/stats)

Next level type declaration check PHPStan rules.

We use these sets to improve code quality of our clients' code beyond PHPStan features.

- These rules make skipped object types explicit, param types narrow and help you to fill more accurate object type hints.
- **They're easy to enable, even if your code does not pass level 0**
- They're effortless to resolve and make your code instantly more solid and reliable.

If you care about code quality and type safety, add these 10 rules to your CI.

Install
-------

[](#install)

```
composer require rector/type-perfect --dev
```

*Note: Make sure you use [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer#usage) to load the necessary service configs or include `vendor/rector/type-perfect/config/extension.neon` file.*

There are 3 checks enabled out of the box. First one makes sure we don't miss a chance to use `instanceof` to make further code know about exact object type:

```
private ?SomeType $someType = null;

if (! empty($this->someType)) {
    // ...
}

if (! isset($this->someType)) {
    // ...
}

// here we only know, that $this->someType is not empty/null
```

🙅

↓

```
if (! $this->someType instanceof SomeType) {
    return;
}

// here we know $this->someType is exactly SomeType
```

✔️

Second rule checks we use explicit object methods over magic array access:

```
$article = new Article();

$id = $article['id'];
// we have no idea, what the type is
```

🙅

↓

```
$id = $article->getId();
// we know the type is int
```

✔️

Last rule checks that all interface implementations follow the same method signature as the interface:

```
interface SomeInterface
{
    public function doSomething(int $value): void;
}

final class SomeClass implements SomeInterface
{
     public function doSomething($value): void { // ... }
}
```

🙅

↓

```
final class SomeClass implements SomeInterface
{
     public function doSomething(int $value): void { // ... }
}
```

✔️

Configure
---------

[](#configure)

Next rules you can enable by configuration. We take them from the simplest to more powerful, in the same order we apply them on legacy projects.

You can enable them all at once:

```
parameters:
    type_perfect:
        no_mixed_property: true
        no_mixed_caller: true
        null_over_false: true
        narrow_param: true
        narrow_return: true
```

Or one by one:

1. Null over False
------------------

[](#1-null-over-false)

```
parameters:
    type_perfect:
        null_over_false: true
```

Bool types are typically used for on/off, yes/no responses. But sometimes, the `false` is misused as *no-result* response, where `null` would be more accurate:

```
public function getProduct()
{
    if (...) {
        return $product;
    }

    return false;
}
```

🙅

↓

We should use `null` instead, as it enabled strict type declaration in form of `?Product` since PHP 7.1:

```
public function getProduct(): ?Product
{
    if (...) {
        return $product;
    }

    return null;
}
```

✔️

2. No mixed Property
--------------------

[](#2-no-mixed-property)

```
parameters:
    type_perfect:
        no_mixed_property: true
```

This rule focuses on PHPStan blind spot while fetching a property. If we have a property with unknown type, PHPStan is not be able to analyse it. It silently ignores it.

```
private $someType;

public function run()
{
    $this->someType->vale;
}
```

It doesn't see there is a typo in `vale` property name. It should be `value`

🙅

↓

```
private SomeType $someType;

public function run()
{
    $this->someType->value;
}
```

This rule makes sure all property fetches know their type they're called on.

✔️

3. No mixed Caller
------------------

[](#3-no-mixed-caller)

```
parameters:
    type_perfect:
        no_mixed_caller: true
```

Same as above, only for method calls:

```
private $someType;

public function run()
{
    $this->someType->someMetho(1, 2);
}
```

It doesn't see there is a typo in `someMetho` name, and that the 2nd parameter must be `string`.

🙅

↓

```
private SomeType $someType;

public function run()
{
    $this->someType->someMethod(1, 'active');
}
```

This group makes sure methods call know their type they're called on.

✔️

4. Narrow Param Types
---------------------

[](#4-narrow-param-types)

The more narrow param type we have, the reliable the code is. `string` beats `mixed`, `int` beats `scalar` and `ExactObject` beats `stdClass`.

```
parameters:
    type_perfect:
        narrow_param: true
```

In case of `private`, but also `public` method calls, our project often knows exact types that are passed in it:

```
// in one file
$product->addPrice(100.52);

// another file
$product->addPrice(52.05);
```

But out of from fear and "just to be safe", we keep the `addPrice()` param type empty, `mixed` or in a docblock.

🙅

↓

If, in 100 % cases the `float` type is passed, PHPStan knows it can be added and improve further analysis:

```
-/**
- * @param float $price
- */
-public function addPrice($price)
+public function addPrice(float $price)
{
    $this->price = $price;
}
```

That's where this group comes in. It checks all the passed types, and tells us know how to narrow the param type declaration.

✔️

5. Narrow Return Types
----------------------

[](#5-narrow-return-types)

Last but not least, the more narrow return type, the more reliable the code.

```
parameters:
    type_perfect:
        narrow_return: true
```

Where does it help? Let's say we have 2 types of talks, that do have different behavior:

```
final class ConferenceTalk extends Talk
{
    public function bookHotel()
    {
        // ...
    }
}

final class MeetupTalk extends Talk
{
    public function bookTrain()
    {
        // ...
    }
}
```

Then we have a factory (repository, or services) that returns generic `Talk` type:

```
final class TalkFactory
{
    public function createConferenceTalk(): Talk
    {
        return new ConferenceTalk();
    }

    public function createMeetupTalk(): Talk
    {
        return new MeetupTalk();
    }
}
```

In this case we've just lost strict type and have to verify the type on runtime:

```
$talk instanceof ConferenceTalk
```

🙅

↓

That's where this group comes in. In case we return the exact type, we should use exact type in return type declaration to keep the code as reliable as possible:

```
 final class TalkFactory
 {
-    public function createConferenceTalk(): Talk
+    public function createConferenceTalk(): ConferenceTalk
     {
         return new ConferenceTalk();
     }

-    public function createMeetupTalk(): Talk
+    public function createMeetupTalk(): MeetupTalk
     {
         return new MeetupTalk();
     }
}
```

✔️

Add sets one by one, fix what you find helpful and ignore the rest.

Happy coding!

###  Health Score

64

—

FairBetter than 99% of packages

Maintenance95

Actively maintained with recent releases

Popularity59

Moderate usage in the ecosystem

Community38

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 55.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~36 days

Recently: every ~87 days

Total

20

Last Release

53d ago

Major Versions

0.2.0 → 1.0.02024-10-01

1.0.0 → v2.x-dev2024-12-12

PHP version history (3 changes)0.1.0PHP ^8.2

0.1.1PHP ^7.2|^8.0

2.0.0PHP ^7.4|^8.0

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/924196?v=4)[Tomas Votruba](/maintainers/TomasVotruba)[@TomasVotruba](https://github.com/TomasVotruba)

---

Top Contributors

[![TomasVotruba](https://avatars.githubusercontent.com/u/924196?v=4)](https://github.com/TomasVotruba "TomasVotruba (42 commits)")[![samsonasik](https://avatars.githubusercontent.com/u/459648?v=4)](https://github.com/samsonasik "samsonasik (15 commits)")[![staabm](https://avatars.githubusercontent.com/u/120441?v=4)](https://github.com/staabm "staabm (14 commits)")[![alexander-schranz](https://avatars.githubusercontent.com/u/1698337?v=4)](https://github.com/alexander-schranz "alexander-schranz (1 commits)")[![scollovati](https://avatars.githubusercontent.com/u/20740642?v=4)](https://github.com/scollovati "scollovati (1 commits)")[![SanderMuller](https://avatars.githubusercontent.com/u/9074391?v=4)](https://github.com/SanderMuller "SanderMuller (1 commits)")[![Curryed](https://avatars.githubusercontent.com/u/16139308?v=4)](https://github.com/Curryed "Curryed (1 commits)")[![mitelg](https://avatars.githubusercontent.com/u/6985627?v=4)](https://github.com/mitelg "mitelg (1 commits)")

---

Tags

phpphpstanrulestest-coveragetype-declaration

### Embed Badge

![Health badge](/badges/rector-type-perfect/health.svg)

```
[![Health](https://phpackages.com/badges/rector-type-perfect/health.svg)](https://phpackages.com/packages/rector-type-perfect)
```

###  Alternatives

[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.4k](/packages/larastan-larastan)[phpstan/phpstan-symfony

Symfony Framework extensions and rules for PHPStan

78973.3M2.0k](/packages/phpstan-phpstan-symfony)[phpstan/phpstan-doctrine

Doctrine extensions for PHPStan

66970.7M1.3k](/packages/phpstan-phpstan-doctrine)[phparkitect/phparkitect

Enforce architectural constraints in your PHP applications

9134.1M24](/packages/phparkitect-phparkitect)[symplify/phpstan-rules

Set of Symplify rules for PHPStan

26612.1M305](/packages/symplify-phpstan-rules)[shipmonk/dead-code-detector

Dead code detector to find unused PHP code via PHPStan extension. Can automatically remove dead PHP code. Supports libraries like Symfony, Doctrine, PHPUnit etc. Detects dead cycles. Can detect dead code that is tested.

4753.1M82](/packages/shipmonk-dead-code-detector)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
