diff --git a/.gitattributes b/.gitattributes index 5d5606da..01e0208b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ /.gitattributes export-ignore /.github/ export-ignore /.gitignore export-ignore +/phpstan.legacy.neon.dist export-ignore /phpstan.neon.dist export-ignore /phpunit.xml.dist export-ignore /phpunit.xml.legacy export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43c9cb3f..9c2278b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,7 @@ jobs: name: PHPStan (PHP ${{ matrix.php }}) runs-on: ubuntu-22.04 strategy: + fail-fast: false matrix: php: - 8.2 @@ -52,3 +53,6 @@ jobs: coverage: none - run: composer install - run: vendor/bin/phpstan + if: ${{ matrix.php >= 7.2 }} + - run: vendor/bin/phpstan --configuration="phpstan.legacy.neon.dist" + if: ${{ matrix.php < 7.2 }} diff --git a/phpstan.legacy.neon.dist b/phpstan.legacy.neon.dist new file mode 100644 index 00000000..5be7a76a --- /dev/null +++ b/phpstan.legacy.neon.dist @@ -0,0 +1,7 @@ +parameters: + ignoreErrors: + - '#Template type T is declared as covariant, but occurs in contravariant position in parameter result of method React\\Promise\\Promise::settle\(\).#' + - '#Template type T is declared as covariant, but occurs in contravariant position in parameter promise of method React\\Promise\\Promise::unwrap\(\).#' + +includes: + - phpstan.neon.dist diff --git a/src/Deferred.php b/src/Deferred.php index 82f66dad..53d945e4 100644 --- a/src/Deferred.php +++ b/src/Deferred.php @@ -2,9 +2,14 @@ namespace React\Promise; +/** + * @template T + */ final class Deferred { - /** @var Promise */ + /** + * @var PromiseInterface + */ private $promise; /** @var callable */ @@ -21,13 +26,16 @@ public function __construct(callable $canceller = null) }, $canceller); } + /** + * @return PromiseInterface + */ public function promise(): PromiseInterface { return $this->promise; } /** - * @param mixed $value + * @param T $value */ public function resolve($value): void { diff --git a/src/Internal/FulfilledPromise.php b/src/Internal/FulfilledPromise.php index 0712f763..b7da9c86 100644 --- a/src/Internal/FulfilledPromise.php +++ b/src/Internal/FulfilledPromise.php @@ -7,14 +7,17 @@ /** * @internal + * + * @template-implements PromiseInterface + * @template-covariant T */ final class FulfilledPromise implements PromiseInterface { - /** @var mixed */ + /** @var T */ private $value; /** - * @param mixed $value + * @param T $value * @throws \InvalidArgumentException */ public function __construct($value = null) @@ -47,9 +50,9 @@ public function catch(callable $onRejected): PromiseInterface public function finally(callable $onFulfilledOrRejected): PromiseInterface { return $this->then(function ($value) use ($onFulfilledOrRejected): PromiseInterface { - return resolve($onFulfilledOrRejected())->then(function () use ($value) { - return $value; - }); + $onFulfilledOrRejected(); + + return resolve($value); }); } diff --git a/src/Internal/RejectedPromise.php b/src/Internal/RejectedPromise.php index cbd8ef53..03088b69 100644 --- a/src/Internal/RejectedPromise.php +++ b/src/Internal/RejectedPromise.php @@ -8,6 +8,9 @@ /** * @internal + * + * @template-implements PromiseInterface + * @template-covariant T */ final class RejectedPromise implements PromiseInterface { @@ -47,9 +50,9 @@ public function catch(callable $onRejected): PromiseInterface public function finally(callable $onFulfilledOrRejected): PromiseInterface { return $this->then(null, function (\Throwable $reason) use ($onFulfilledOrRejected): PromiseInterface { - return resolve($onFulfilledOrRejected())->then(function () use ($reason): PromiseInterface { - return new RejectedPromise($reason); - }); + $onFulfilledOrRejected(); + + return new RejectedPromise($reason); }); } diff --git a/src/Promise.php b/src/Promise.php index a2d72b6d..790fb94c 100644 --- a/src/Promise.php +++ b/src/Promise.php @@ -4,12 +4,16 @@ use React\Promise\Internal\RejectedPromise; +/** + * @template-implements PromiseInterface + * @template-covariant T + */ final class Promise implements PromiseInterface { /** @var ?callable */ private $canceller; - /** @var ?PromiseInterface */ + /** @var ?PromiseInterface */ private $result; /** @var callable[] */ @@ -77,13 +81,13 @@ public function catch(callable $onRejected): PromiseInterface public function finally(callable $onFulfilledOrRejected): PromiseInterface { return $this->then(static function ($value) use ($onFulfilledOrRejected) { - return resolve($onFulfilledOrRejected())->then(function () use ($value) { - return $value; - }); + $onFulfilledOrRejected(); + + return $value; }, static function ($reason) use ($onFulfilledOrRejected) { - return resolve($onFulfilledOrRejected())->then(function () use ($reason) { - return new RejectedPromise($reason); - }); + $onFulfilledOrRejected(); + + return new RejectedPromise($reason); }); } @@ -166,6 +170,10 @@ private function reject(\Throwable $reason): void $this->settle(reject($reason)); } + /** + * Test out if null can be promise + * @param PromiseInterface|PromiseInterface $result + */ private function settle(PromiseInterface $result): void { $result = $this->unwrap($result); @@ -193,13 +201,17 @@ private function settle(PromiseInterface $result): void } } + /** + * @param PromiseInterface|PromiseInterface $promise + * @return PromiseInterface + */ private function unwrap(PromiseInterface $promise): PromiseInterface { while ($promise instanceof self && null !== $promise->result) { $promise = $promise->result; } - return $promise; + return $promise; /** @phpstan-ignore-line */ } private function call(callable $cb): void diff --git a/src/PromiseInterface.php b/src/PromiseInterface.php index 47117072..2430a2ed 100644 --- a/src/PromiseInterface.php +++ b/src/PromiseInterface.php @@ -2,6 +2,9 @@ namespace React\Promise; +/** + * @template-covariant T + */ interface PromiseInterface { /** @@ -28,9 +31,9 @@ interface PromiseInterface * 2. `$onFulfilled` and `$onRejected` will never be called more * than once. * - * @param callable|null $onFulfilled - * @param callable|null $onRejected - * @return PromiseInterface + * @template TFulfilled as PromiseInterface|T|void + * @param (callable(T): TFulfilled)|null $onFulfilled + * @return PromiseInterface */ public function then(?callable $onFulfilled = null, ?callable $onRejected = null): PromiseInterface; @@ -44,8 +47,7 @@ public function then(?callable $onFulfilled = null, ?callable $onRejected = null * Additionally, you can type hint the `$reason` argument of `$onRejected` to catch * only specific errors. * - * @param callable $onRejected - * @return PromiseInterface + * @return PromiseInterface */ public function catch(callable $onRejected): PromiseInterface; @@ -91,8 +93,8 @@ public function catch(callable $onRejected): PromiseInterface; * ->finally('cleanup'); * ``` * - * @param callable $onFulfilledOrRejected - * @return PromiseInterface + * @param callable(): void $onFulfilledOrRejected + * @return PromiseInterface */ public function finally(callable $onFulfilledOrRejected): PromiseInterface; @@ -118,7 +120,7 @@ public function cancel(): void; * ``` * * @param callable $onRejected - * @return PromiseInterface + * @return PromiseInterface * @deprecated 3.0.0 Use catch() instead * @see self::catch() */ @@ -135,7 +137,7 @@ public function otherwise(callable $onRejected): PromiseInterface; * ``` * * @param callable $onFulfilledOrRejected - * @return PromiseInterface + * @return PromiseInterface * @deprecated 3.0.0 Use finally() instead * @see self::finally() */ diff --git a/src/functions.php b/src/functions.php index c42b715e..ffdf1315 100644 --- a/src/functions.php +++ b/src/functions.php @@ -17,8 +17,9 @@ * * If `$promiseOrValue` is a promise, it will be returned as is. * - * @param mixed $promiseOrValue - * @return PromiseInterface + * @template T + * @param PromiseInterface|T $promiseOrValue + * @return PromiseInterface */ function resolve($promiseOrValue): PromiseInterface { @@ -30,6 +31,7 @@ function resolve($promiseOrValue): PromiseInterface $canceller = null; if (\method_exists($promiseOrValue, 'cancel')) { + /** @var callable $canceller */ $canceller = [$promiseOrValue, 'cancel']; } @@ -54,8 +56,7 @@ function resolve($promiseOrValue): PromiseInterface * throwing an exception. For example, it allows you to propagate a rejection with * the value of another promise. * - * @param \Throwable $reason - * @return PromiseInterface + * @return PromiseInterface */ function reject(\Throwable $reason): PromiseInterface { @@ -68,8 +69,9 @@ function reject(\Throwable $reason): PromiseInterface * will be an array containing the resolution values of each of the items in * `$promisesOrValues`. * - * @param iterable $promisesOrValues - * @return PromiseInterface + * @template T + * @param iterable|T> $promisesOrValues + * @return PromiseInterface> */ function all(iterable $promisesOrValues): PromiseInterface { @@ -119,8 +121,9 @@ function (\Throwable $reason) use (&$continue, $reject): void { * The returned promise will become **infinitely pending** if `$promisesOrValues` * contains 0 items. * - * @param iterable $promisesOrValues - * @return PromiseInterface + * @template T + * @param iterable|T> $promisesOrValues + * @return PromiseInterface */ function race(iterable $promisesOrValues): PromiseInterface { @@ -154,8 +157,9 @@ function race(iterable $promisesOrValues): PromiseInterface * The returned promise will also reject with a `React\Promise\Exception\LengthException` * if `$promisesOrValues` contains 0 items. * - * @param iterable $promisesOrValues - * @return PromiseInterface + * @template T + * @param iterable|T> $promisesOrValues + * @return PromiseInterface */ function any(iterable $promisesOrValues): PromiseInterface { diff --git a/tests/FunctionAnyTest.php b/tests/FunctionAnyTest.php index 563f882e..09c4b34b 100644 --- a/tests/FunctionAnyTest.php +++ b/tests/FunctionAnyTest.php @@ -52,6 +52,7 @@ public function shouldResolveWithAnInputValue(): void ->expects(self::once()) ->method('__invoke') ->with(self::identicalTo(1)); + assert(is_callable($mock)); any([1, 2, 3]) ->then($mock); diff --git a/tests/Internal/CancellationQueueTest.php b/tests/Internal/CancellationQueueTest.php index c2907f73..fea5696f 100644 --- a/tests/Internal/CancellationQueueTest.php +++ b/tests/Internal/CancellationQueueTest.php @@ -96,6 +96,9 @@ public function rethrowsExceptionsThrownFromCancel(): void $cancellationQueue(); } + /** + * @return Deferred + */ private function getCancellableDeferred(): Deferred { return new Deferred($this->expectCallableOnce()); diff --git a/tests/Internal/FulfilledPromiseTest.php b/tests/Internal/FulfilledPromiseTest.php index 073b9d7a..390ffad0 100644 --- a/tests/Internal/FulfilledPromiseTest.php +++ b/tests/Internal/FulfilledPromiseTest.php @@ -9,6 +9,9 @@ use React\Promise\PromiseTest\PromiseSettledTestTrait; use React\Promise\TestCase; +/** + * @template T + */ class FulfilledPromiseTest extends TestCase { use PromiseSettledTestTrait, @@ -16,7 +19,7 @@ class FulfilledPromiseTest extends TestCase public function getPromiseTestAdapter(callable $canceller = null): CallbackPromiseAdapter { - /** @var ?FulfilledPromise */ + /** @var ?FulfilledPromise */ $promise = null; return new CallbackPromiseAdapter([ diff --git a/tests/Internal/RejectedPromiseTest.php b/tests/Internal/RejectedPromiseTest.php index 72cef091..d1ac09d5 100644 --- a/tests/Internal/RejectedPromiseTest.php +++ b/tests/Internal/RejectedPromiseTest.php @@ -9,6 +9,9 @@ use React\Promise\PromiseTest\PromiseSettledTestTrait; use React\Promise\TestCase; +/** + * @template T + */ class RejectedPromiseTest extends TestCase { use PromiseSettledTestTrait, @@ -16,7 +19,7 @@ class RejectedPromiseTest extends TestCase public function getPromiseTestAdapter(callable $canceller = null): CallbackPromiseAdapter { - /** @var ?RejectedPromise */ + /** @var ?RejectedPromise */ $promise = null; return new CallbackPromiseAdapter([ diff --git a/tests/PromiseAdapter/CallbackPromiseAdapter.php b/tests/PromiseAdapter/CallbackPromiseAdapter.php index 14a0acd4..aec72949 100644 --- a/tests/PromiseAdapter/CallbackPromiseAdapter.php +++ b/tests/PromiseAdapter/CallbackPromiseAdapter.php @@ -17,6 +17,9 @@ public function __construct(array $callbacks) $this->callbacks = $callbacks; } + /** + * @phpstan-ignore-next-line + */ public function promise(): PromiseInterface { return ($this->callbacks['promise'])(...func_get_args()); diff --git a/tests/PromiseAdapter/PromiseAdapterInterface.php b/tests/PromiseAdapter/PromiseAdapterInterface.php index 727fd514..0eaa2dcb 100644 --- a/tests/PromiseAdapter/PromiseAdapterInterface.php +++ b/tests/PromiseAdapter/PromiseAdapterInterface.php @@ -6,6 +6,9 @@ interface PromiseAdapterInterface { + /** + * @phpstan-ignore-next-line + */ public function promise(): PromiseInterface; public function resolve(): void; public function reject(): void; diff --git a/tests/PromiseTest/PromiseFulfilledTestTrait.php b/tests/PromiseTest/PromiseFulfilledTestTrait.php index d982214a..59c7387a 100644 --- a/tests/PromiseTest/PromiseFulfilledTestTrait.php +++ b/tests/PromiseTest/PromiseFulfilledTestTrait.php @@ -191,7 +191,7 @@ public function thenShouldContinueToExecuteCallbacksWhenPriorCallbackSuspendsFib $adapter->resolve(42); $fiber = new \Fiber(function () use ($adapter) { - $adapter->promise()->then(function (int $value) { + $adapter->promise()->then(function (int $value) { /** @phpstan-ignore-line */ \Fiber::suspend($value); }); }); @@ -261,7 +261,7 @@ public function finallyShouldNotSuppressValueWhenHandlerReturnsANonPromiseForFul $adapter->resolve($value); $adapter->promise() - ->finally(function () { + ->finally(function () { /** @phpstan-ignore-line */ return 1; }) ->then($mock); @@ -282,7 +282,7 @@ public function finallyShouldNotSuppressValueWhenHandlerReturnsAPromiseForFulfil $adapter->resolve($value); $adapter->promise() - ->finally(function () { + ->finally(function () { /** @phpstan-ignore-line */ return resolve(1); }) ->then($mock); @@ -324,7 +324,7 @@ public function finallyShouldRejectWhenHandlerRejectsForFulfilledPromise(): void $adapter->resolve(1); $adapter->promise() - ->finally(function () use ($exception) { + ->finally(function () use ($exception) { /** @phpstan-ignore-line */ return reject($exception); }) ->then(null, $mock); diff --git a/tests/PromiseTest/PromiseRejectedTestTrait.php b/tests/PromiseTest/PromiseRejectedTestTrait.php index af8bcaf7..9a094d41 100644 --- a/tests/PromiseTest/PromiseRejectedTestTrait.php +++ b/tests/PromiseTest/PromiseRejectedTestTrait.php @@ -298,7 +298,7 @@ public function finallyShouldNotSuppressRejectionWhenHandlerReturnsANonPromiseFo $adapter->reject($exception); $adapter->promise() - ->finally(function () { + ->finally(function () { /** @phpstan-ignore-line */ return 1; }) ->then(null, $mock); @@ -319,7 +319,7 @@ public function finallyShouldNotSuppressRejectionWhenHandlerReturnsAPromiseForRe $adapter->reject($exception); $adapter->promise() - ->finally(function () { + ->finally(function () { /** @phpstan-ignore-line */ return resolve(1); }) ->then(null, $mock); @@ -363,7 +363,7 @@ public function finallyShouldRejectWhenHandlerRejectsForRejectedPromise(): void $adapter->reject($exception1); $adapter->promise() - ->finally(function () use ($exception2) { + ->finally(function () use ($exception2) { /** @phpstan-ignore-line */ return reject($exception2); }) ->then(null, $mock); @@ -504,7 +504,7 @@ public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromiseFor $adapter->reject($exception); $adapter->promise() - ->finally(function () { + ->finally(function () { /** @phpstan-ignore-line */ return 1; }) ->then(null, $mock); diff --git a/tests/PromiseTest/RejectTestTrait.php b/tests/PromiseTest/RejectTestTrait.php index ad55ca28..aeb65538 100644 --- a/tests/PromiseTest/RejectTestTrait.php +++ b/tests/PromiseTest/RejectTestTrait.php @@ -73,7 +73,7 @@ public function rejectShouldMakePromiseImmutable(): void ->with($this->identicalTo($exception1)); $adapter->promise() - ->then(null, function ($value) use ($exception3, $adapter) { + ->then(null, function (\Throwable $value) use ($exception3, $adapter) { $adapter->reject($exception3); return reject($value); @@ -140,7 +140,7 @@ public function finallyShouldNotSuppressRejectionWhenHandlerReturnsANonPromise() ->with($this->identicalTo($exception)); $adapter->promise() - ->finally(function () { + ->finally(function () { /** @phpstan-ignore-line */ return 1; }) ->then(null, $mock); @@ -162,7 +162,7 @@ public function finallyShouldNotSuppressRejectionWhenHandlerReturnsAPromise(): v ->with($this->identicalTo($exception)); $adapter->promise() - ->finally(function () { + ->finally(function () { /** @phpstan-ignore-line */ return resolve(1); }) ->then(null, $mock); @@ -206,7 +206,7 @@ public function finallyShouldRejectWhenHandlerRejectsForRejection(): void ->with($this->identicalTo($exception)); $adapter->promise() - ->finally(function () use ($exception) { + ->finally(function () use ($exception) { /** @phpstan-ignore-line */ return reject($exception); }) ->then(null, $mock); diff --git a/tests/PromiseTest/ResolveTestTrait.php b/tests/PromiseTest/ResolveTestTrait.php index 357fdedc..62624f2e 100644 --- a/tests/PromiseTest/ResolveTestTrait.php +++ b/tests/PromiseTest/ResolveTestTrait.php @@ -200,7 +200,7 @@ public function finallyShouldNotSuppressValueWhenHandlerReturnsANonPromise(): vo ->with($this->identicalTo($value)); $adapter->promise() - ->finally(function () { + ->finally(function () { /** @phpstan-ignore-line */ return 1; }) ->then($mock); @@ -222,7 +222,7 @@ public function finallyShouldNotSuppressValueWhenHandlerReturnsAPromise(): void ->with($this->identicalTo($value)); $adapter->promise() - ->finally(function () { + ->finally(function () { /** @phpstan-ignore-line */ return resolve(1); }) ->then($mock); @@ -266,7 +266,7 @@ public function finallyShouldRejectWhenHandlerRejectsForFulfillment(): void ->with($this->identicalTo($exception)); $adapter->promise() - ->finally(function () use ($exception) { + ->finally(function () use ($exception) { /** @phpstan-ignore-line */ return reject($exception); }) ->then(null, $mock); diff --git a/tests/types/basic.php b/tests/types/basic.php new file mode 100644 index 00000000..13b20bf1 --- /dev/null +++ b/tests/types/basic.php @@ -0,0 +1,16 @@ +', resolve(true)); +assertType('React\Promise\PromiseInterface', resolve(Types\stringOrInt())); +assertType('React\Promise\PromiseInterface', Types\stringOrIntPromise()); +assertType('React\Promise\PromiseInterface', resolve(resolve(true))); diff --git a/tests/types/chaining.php b/tests/types/chaining.php new file mode 100644 index 00000000..7b221945 --- /dev/null +++ b/tests/types/chaining.php @@ -0,0 +1,32 @@ +', resolve(true)->then('React\Promise\Types\passThroughBoolFn')); +assertType('React\Promise\PromiseInterface', resolve(true)->then()->then('React\Promise\Types\passThroughBoolFn')); +assertType('React\Promise\PromiseInterface', resolve(true)->then(null)->then('React\Promise\Types\passThroughBoolFn')); +assertType('React\Promise\PromiseInterface', resolve(true)->then('React\Promise\Types\passThroughBoolFn')->then('React\Promise\Types\passThroughBoolFn')); +assertType('React\Promise\PromiseInterface', resolve(true)->then('React\Promise\Types\passThroughBoolFn', 'React\Promise\Types\passThroughThrowable')->then('React\Promise\Types\passThroughBoolFn')); +assertType('React\Promise\PromiseInterface', resolve(true)->then(null, 'React\Promise\Types\passThroughThrowable')->then('React\Promise\Types\passThroughBoolFn')); +assertType('React\Promise\PromiseInterface', resolve(true)->then()->then(null, 'React\Promise\Types\passThroughThrowable')->then('React\Promise\Types\passThroughBoolFn')); + +/** + * Changing types while chaining + */ +assertType('React\Promise\PromiseInterface', resolve(true)->then(function (bool $bool): int { + return time(); +})); +assertType('React\Promise\PromiseInterface', resolve(true)->then('React\Promise\Types\flipBoolToInt')); diff --git a/tests/types/common.php b/tests/types/common.php new file mode 100644 index 00000000..9c806443 --- /dev/null +++ b/tests/types/common.php @@ -0,0 +1,45 @@ + + */ +function passThroughThrowable(Throwable $t): PromiseInterface { + return reject($t); +}; + +/** + * @return int|string + */ +function stringOrInt() { + return time() % 2 ? 'string' : time(); +}; + +/** + * @return PromiseInterface + */ +function stringOrIntPromise(): PromiseInterface { + return resolve(time() % 2 ? 'string' : time()); +}; + +/** + * @return PromiseInterface + */ +function flipBoolToInt(bool $bool): PromiseInterface { + return resolve(time()); +}; + +function theMeaningOfLifeTheUniverseAndEverything(): int { + return 42; +}; diff --git a/tests/types/deferred.php b/tests/types/deferred.php new file mode 100644 index 00000000..f8bf93bd --- /dev/null +++ b/tests/types/deferred.php @@ -0,0 +1,11 @@ +', $deferredA->promise()); + +$deferredB = new Deferred(); +$deferredB->resolve(42); +assertType('React\Promise\PromiseInterface', $deferredB->promise()); diff --git a/tests/types/deprecated.php b/tests/types/deprecated.php new file mode 100644 index 00000000..3f3e68e7 --- /dev/null +++ b/tests/types/deprecated.php @@ -0,0 +1,19 @@ +', new FulfilledPromise(true)); +assertType('React\Promise\PromiseInterface', (new FulfilledPromise(true))->then('React\Promise\Types\passThroughBoolFn')); + +/** + * reject + */ +assertType('React\Promise\PromiseInterface<*NEVER*>', new RejectedPromise(new RuntimeException())); diff --git a/tests/types/functions_all.php b/tests/types/functions_all.php new file mode 100644 index 00000000..44b643e0 --- /dev/null +++ b/tests/types/functions_all.php @@ -0,0 +1,15 @@ +>', all([resolve(true), resolve(false)])); +assertType('React\Promise\PromiseInterface>', all([resolve(true), false])); +assertType('React\Promise\PromiseInterface>', all([true, time()])); +assertType('React\Promise\PromiseInterface>', all([resolve(true), resolve(time())])); diff --git a/tests/types/functions_any.php b/tests/types/functions_any.php new file mode 100644 index 00000000..78659dfe --- /dev/null +++ b/tests/types/functions_any.php @@ -0,0 +1,15 @@ +', any([resolve(true), resolve(false)])); +assertType('React\Promise\PromiseInterface', any([resolve(true), false])); +assertType('React\Promise\PromiseInterface', any([true, time()])); +assertType('React\Promise\PromiseInterface', any([resolve(true), resolve(time())])); diff --git a/tests/types/functions_race.php b/tests/types/functions_race.php new file mode 100644 index 00000000..e3e12af0 --- /dev/null +++ b/tests/types/functions_race.php @@ -0,0 +1,15 @@ +', race([resolve(true), resolve(false)])); +assertType('React\Promise\PromiseInterface', race([resolve(true), false])); +assertType('React\Promise\PromiseInterface', race([true, time()])); +assertType('React\Promise\PromiseInterface', race([resolve(true), resolve(time())])); diff --git a/tests/types/reject.php b/tests/types/reject.php new file mode 100644 index 00000000..a4fc1c5e --- /dev/null +++ b/tests/types/reject.php @@ -0,0 +1,11 @@ +', reject(new RuntimeException())); +assertType('React\Promise\PromiseInterface<*NEVER*>', reject(new RuntimeException())->then('React\Promise\Types\theMeaningOfLifeTheUniverseAndEverything')); +assertType('React\Promise\PromiseInterface', reject(new RuntimeException())->then(null, 'React\Promise\Types\theMeaningOfLifeTheUniverseAndEverything')); +assertType('React\Promise\PromiseInterface', reject(new RuntimeException())->catch('React\Promise\Types\theMeaningOfLifeTheUniverseAndEverything'));