promise icon indicating copy to clipboard operation
promise copied to clipboard

[2.x] Add basic template annotations

Open WyriHaximus opened this issue 3 years ago • 4 comments

This adds basic type safety annotations for static analyzers like PHPStan and Psalm. This will cover around 80% of the use cases and a follow-up PR for all supported versions will be proposed later to get it to a 100% of close to a 100%.

By adding these annotations methods returning a promise can hint their resolving type by adding @return PromiseInterface<bool> when they for example resolve to a boolean. By doing that Psalm and PHPStan will understand that the following bit of code will not become an issue because the method's contract promised a boolean through the promise:

$promise->then(static function (bool $isEnabled) {});

However, the following will yield errors:

$promise->then(static function (string $isEnabled) {});

This PR is a requirement for https://github.com/reactphp/async/pull/40

WyriHaximus avatar Jun 21 '22 21:06 WyriHaximus

@clue Updated the PR with a types directory for PHPStan type checking, and expanded the annotations a bit

WyriHaximus avatar Jun 23 '22 23:06 WyriHaximus

Another thing is that I believe the promise type is covariant on T (and R): https://github.com/webonyx/graphql-php/pull/1170#issuecomment-1155043969

vhenzl avatar Jun 29 '22 11:06 vhenzl

@vhenzl The main goal with this PR is to make it work well enough to make https://github.com/reactphp/async/pull/40 possible and then make the rest fully work in https://github.com/reactphp/promise/pull/223. I'll take a good read of your comment and playgrounds tonight, and do a longer response. But if you have a way to reach the goal I mentioned above then please feel free to suggest it as we realised this is a bigger undertaking than just this PR. And we want to get fibers out in the short term with typing.

WyriHaximus avatar Jun 29 '22 11:06 WyriHaximus

I'm not convinced that it's worth trying to type promise with two generic types, nor I'm sure that's actually possible to do it properly. Having a single template type for the fulfilled value like TypeScript has would be much easier: https://github.com/microsoft/TypeScript/blob/2ecde2718722d6643773d43390aa57c3e3199365/lib/lib.es5.d.ts#L1446-L1461

@vhenzl Excellent input and indeed something we've considered before. I agree that having the TFulfilled provides most value here and whether or not TRejected is useful is still debatable, so perhaps we should indeed focus on TFulfilled only for now. Implementing full type safety is indeed somewhat harder than it may seem at first sight, so I'd rather start somewhere and build from there.

After the discussion so far, it's my understanding focusing solely on TFulfilled (as also originally suggested by @WyriHaximus prior to my feedback above) would simplify this PR significantly, probably solve like 90% of use cases and can still serve as a good starting point if we find a need to provide type safe rejection values.

clue avatar Jun 29 '22 15:06 clue

Closing this in favour of #223

WyriHaximus avatar May 02 '23 12:05 WyriHaximus