1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-11 16:59:45 +01:00
psalm/docs/annotating_code/typing_in_psalm.md

174 lines
4.0 KiB
Markdown
Raw Normal View History

2018-02-18 01:53:17 +01:00
# Typing in Psalm
Psalm is able to interpret all PHPDoc type annotations, and use them to further understand the codebase.
2019-03-19 18:55:55 +01:00
Types are used to describe acceptable values for properties, variables, function parameters and `return $x`.
2018-02-18 01:53:17 +01:00
2019-06-10 00:05:28 +02:00
## Docblock Type Syntax
2020-04-02 15:40:17 +02:00
Psalm allows you to express a lot of complicated type information in docblocks.
All docblock types are either [atomic types](type_syntax/atomic_types.md), [union types](type_syntax/union_types.md) or [intersection types](type_syntax/intersection_types.md).
2022-04-27 07:46:13 +02:00
Additionally, Psalm supports PHPDocs [type syntax](https://docs.phpdoc.org/latest/guide/guides/types.html), and also the [proposed PHPDoc PSR type syntax](https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md#appendix-a-types).
2018-02-18 01:53:17 +01:00
## Property declaration types vs Assignment typehints
You can use the `/** @var Type */` docblock to annotate both [property declarations](http://php.net/manual/en/language.oop5.properties.php) and to help Psalm understand variable assignment.
### Property declaration types
You can specify a particular type for a class property declaration in Psalm by using the `@var` declaration:
2018-02-18 01:53:17 +01:00
```php
<?php
2018-02-18 01:53:17 +01:00
/** @var string|null */
public $foo;
```
When checking `$this->foo = $some_variable;`, Psalm will check to see whether `$some_variable` is either `string` or `null` and, if neither, emit an issue.
If you leave off the property type docblock, Psalm will emit a `MissingPropertyType` issue.
### Assignment typehints
Consider the following code:
```php
<?php
2019-01-03 12:47:10 +01:00
namespace YourCode {
function bar() : int {
$a = \ThirdParty\foo();
2018-02-18 01:53:17 +01:00
return $a;
}
2019-01-03 12:47:10 +01:00
}
namespace ThirdParty {
function foo() {
return mt_rand(0, 100);
2018-02-18 01:53:17 +01:00
}
}
```
2019-01-03 12:47:10 +01:00
Psalm does not know what the third-party function `ThirdParty\foo` returns, because the author has not added any return types. If you know that the function returns a given value you can use an assignment typehint like so:
2018-02-18 01:53:17 +01:00
```php
<?php
2019-01-03 12:47:10 +01:00
namespace YourCode {
function bar() : int {
/** @var int */
$a = \ThirdParty\foo();
2018-02-18 01:53:17 +01:00
return $a;
}
2019-01-03 12:47:10 +01:00
}
namespace ThirdParty {
function foo() {
return mt_rand(0, 100);
2018-02-18 01:53:17 +01:00
}
}
```
This tells Psalm that `int` is a possible type for `$a`, and allows it to infer that `return $a;` produces an integer.
Unlike property types, however, assignment typehints are not binding they can be overridden by a new assignment without Psalm emitting an issue e.g.
```php
<?php
2018-02-18 01:53:17 +01:00
/** @var string|null */
$a = foo();
$a = 6; // $a is now typed as an int
```
You can also use typehints on specific variables e.g.
```php
<?php
2018-02-18 01:53:17 +01:00
/** @var string $a */
echo strpos($a, 'hello');
```
This tells Psalm to assume that `$a` is a string (though it will still throw an error if `$a` is undefined).
2018-05-24 20:31:55 +02:00
## Specifying string/int options (aka enums)
2018-02-18 01:53:17 +01:00
2018-05-24 20:31:55 +02:00
Psalm allows you to specify a specific set of allowed string/int values for a given function or method.
2018-02-18 01:53:17 +01:00
2018-05-24 20:31:55 +02:00
Whereas this would cause Psalm to [complain that not all paths return a value](https://getpsalm.org/r/9f6f1ceab6):
```php
<?php
2018-05-24 20:31:55 +02:00
function foo(string $s) : string {
switch ($s) {
case 'a':
return 'hello';
case 'b':
return 'goodbye';
}
}
```
If you specify the param type of `$s` as `'a'|'b'` Psalm will know that all paths return a value:
```php
<?php
2018-05-24 20:31:55 +02:00
/**
* @param 'a'|'b' $s
*/
function foo(string $s) : string {
switch ($s) {
case 'a':
return 'hello';
case 'b':
return 'goodbye';
}
}
```
If the values are in class constants, you can use those too:
```php
<?php
2018-05-24 20:31:55 +02:00
class A {
2018-05-24 20:33:41 +02:00
const FOO = 'foo';
const BAR = 'bar';
2018-05-24 20:31:55 +02:00
}
/**
2019-06-10 00:05:28 +02:00
* @param A::FOO | A::BAR $s
2018-05-24 20:31:55 +02:00
*/
function foo(string $s) : string {
switch ($s) {
case A::FOO:
return 'hello';
case A::BAR:
return 'goodbye';
}
}
```
2020-02-03 17:07:48 +01:00
If the class constants share a common prefix, you can specify them all using a wildcard:
```php
<?php
2020-02-03 17:07:48 +01:00
class A {
const STATUS_FOO = 'foo';
const STATUS_BAR = 'bar';
}
/**
* @param A::STATUS_* $s
*/
function foo(string $s) : string {
switch ($s) {
case A::STATUS_FOO:
return 'hello';
default:
// any other status
return 'goodbye';
}
}
```