# Conditional types

Psalm supports the equivalent of TypeScript’s conditional types.

Conditional types have the form:

`(<template param> is <union type> ? <union type> : <union type>)`

All conditional types must be wrapped inside brackets e.g. `(...)`

Conditional types are dependent on template parameters, so you can only use them in a function where template parameters are defined.

## Example application

Let's suppose we want to make a userland implementation of PHP's numeric addition (but please never do this). You could type this with a conditional return type:

```
<?php
/**
* @template T of int|float
* @param T $a
* @param T $b
* @return int|float
* @psalm-return (T is int ? int : float)
*/
function add($a, $b) {
return $a + $b;
}
```

When figuring out the result of `add($x, $y)`

Psalm tries to infer the value `T`

for that particular call. When calling `add(1, 2)`

, `T`

can be trivially inferred as an `int`

. Then Psalm takes the provided conditional return type

`(T is int ? int : float)`

and substitutes in the known value of `T`

, `int`

, so that expression becomes

`(int is int ? int : float)`

which simplifies to `(true ? int : float)`

, which simplifies to `int`

.

Calling `add(1, 2.1)`

means `T`

would instead be inferred as `int|float`

, which means the expression `(T is int ? int : float)`

would instead have the substitution

`(int|float is int ? int : float)`

The union `int|float`

is clearly not an `int`

, so the expression is simplified to `(false ? int : float)`

, which simplifies to `float`

.

## Nested conditionals

You can also nest conditionals just as you could ternary expressions:

```
<?php
class A {
const TYPE_STRING = 0;
const TYPE_INT = 1;
/**
* @template T of int
* @param T $i
* @psalm-return (
* T is self::TYPE_STRING
* ? string
* : (T is self::TYPE_INT ? int : bool)
* )
*/
public static function getDifferentType(int $i) {
if ($i === self::TYPE_STRING) {
return "hello";
}
if ($i === self::TYPE_INT) {
return 5;
}
return true;
}
}
```