

Union Types vs. Intersection Types – Ondřej Mirtes – Medium
source link: https://medium.com/@ondrejmirtes/union-types-vs-intersection-types-fd44a8eacbb
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.

Union Types vs. Intersection Types
One of the headlining features of PHPStan 0.9 is the introduction of intersection types. Since this is a very useful feature that helps us understand the code much better, but the terminology is largely unknown and mysterious to the PHP community, I decided to write up and compare these two kinds of compound types.
Union Types
- Written as Foo|Bar. Common syntax used in phpDocs for many years.
- It means that the type of a variable is either Foo or Bar.
- When a function accepts Foo|Bar as an argument, it means that only properties and methods that are available on both Foo and Bar can be safely accessed on the variable. If Foo has a $loremProperty, you can access it only if Bar has the same property.
- It's dangerous to pass value of type Foo|Bar to a function parameter where only Foo is allowed, because if it's Bar in runtime, the called function does not accept it.
- It's fine to pass value of type Foo to a function parameter of type Foo|Bar, because Foo is one of more allowed options.
The most common way of utilising union types is in function arguments that accept multiple different types. They can be compared with instanceof or a comparison operator to trim down the possibilities when working with them:
/**
* @param Foo|Bar $object
*/
public function doSomethingUseful($object)
{
if ($object instanceof Foo) {
// now we can be sure that $object is just Foo in this branch
} elseif ($object instanceof Bar) {
// dtto for Bar
}
}
Do not confuse union types with the way developers usually mark types of items in a collection (that's usually an iterator):
/**
* @param Collection|Foo[] $object
*/
public function doSomethingUseful($object)
{
foreach ($object as $foo) {
// $foo is Foo here
}
}
Intersection Types
- Written as Foo&Bar. This is a very rare syntax seen in the wild and it might not even be supported by your IDE. It's because when people write Foo|Bar, they sometimes mean a union type and sometimes an intersection type by that. Let's hope that this changes in the following months and years (not only) thanks to PHPStan. Differentiating between them helps the code to be more understandable and also more friendly to static analyzers which benefits everyone.
- It means that the type of a variable is Foo and Bar at the same time.
- When a function accepts Foo&Bar as an argument, it means that properties and methods from either type can be safely accessed. You can access properties and call methods from Foo even if they are not on Bar.
- It's fine to pass value of type Foo&Bar to a parameter of type Foo.
- It's dangerous to pass value of type Foo to a parameter of type Foo&Bar, because someone might access a property or call a method from Bar.
- It's fine to pass value of type Foo&Bar&Baz to a parameter of type Foo&Bar, type Baz is simply thrown away in this case.
The thing is that you're already creating intersection types in your code and not even realize it! Consider this code:
public function doSomethingUseful(Foo $object)
{
if ($object instanceof BarInterface) {
// $foo is Foo&BarInterface here!
}
}
Other use case for them are mock objects in unit-testing frameworks, typically PHPUnit. Think about it: you create an object and it's possible to call methods on it from both mocked class and mock-specific configuration methods!
class Foo
{
public function doFooStuff()
{
}
}class SomeTest extends \PHPUnit\Framework\TestCase
{
public function testSomething()
{
$mockedFoo = $this–>createMock(Foo::class);
// $mockedFoo is Foo&PHPUnit_Framework_MockObject_MockObject // we can call mock-configuration methods:
$mockedFoo->method('doFooStuff')
->will($this->returnValue('fooResult'));
// and also methods from Foo itself:
$mockedFoo->doFooStuff();
}
}
You can also take advantage of intersection types if you don't want to tie your code to a specific class, but want to typehint multiple interfaces at once. For example, if you want to safely iterate over an object and at the same time pass it to count(), you can do it like this:
/**
* @param \Traversable&\Countable $object
*/
public function doSomethingUseful($object)
{
echo sprintf(
'We are going to iterate over %d values!',
count($object)
);
foreach ($object as $foo) {
}
}
This is really nice because it supports designing your codebase with small and simple interfaces.
If you’re a PHP developer, give PHPStan a shot. If you’re interested in various insights about software development, follow me on Twitter. If you’re interested in my consulting services on code quality, continuous delivery, hiring developers, open-source and plethora of other topics, please get in touch.
Recommend
-
105
PHPStan 0.9: A Huge Leap ForwardIt's been a year since I unveiled PHPStan to the world. After 19 releases,...
-
69
On December 4th 2016, I released a first version of PHPStan to the public. Two years, 2,5 million downloads and 5,400 GitHub stars later, it’s going strong and I have many ideas how to improve it. My…
-
33
Leveraging Intersection Types in JavaPublished July 6, 2019Intersection types are one of these relatively unknown and underestimated advanced features...
-
74
Once a year, new minor PHP version is released, and every few years, there’s a new major version. And as the language moves forward, the tooling that goes with it has to move forward too. That’s why I just released
-
52
So what’s new in PHPStan 0.12?GenericsThe flagship feature of this release. It deserves its own article — check it o...
-
10
Do you know about Intersection Types in Scala 3.0, Dotty? Reading Time: 2 minutes You might have learnt intersection of su...
-
6
Rob and Jason are joined by Ondřej Čertík from Los Alamos National Laboratory. They first talk about ISO Papers and Github's CoPilot AI programmer. Then they talk to Ondřej about LFortran, a modern LLVM based Fortran compiler that can compil...
-
10
The in-situ versions of set_difference, set_intersection, and set_union advertisements I implemented versions of set_union,
-
5
How to implement a TypeScript-style type checker (Reconstructing TypeScript), part 5Jake Donham > Technical Difficulties > Reconstructing TypeScript, part...
-
6
NOTE: 集合類型論 -- 差集類型今天我想介紹一篇論文 Programming with union, intersection, and negation types* 其中差集類型的概念,而紀錄下這篇文章。 我們已經很習慣各式各樣的...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK