Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core-paging] Use a TS v3.4-friendly iterator protocol #19507

Merged
merged 4 commits into from
Jan 5, 2022

Conversation

deyaaeldeen
Copy link
Member

@deyaaeldeen deyaaeldeen commented Dec 22, 2021

Relates to #19394 (fixes it but we do not want to close it after merging)

Updates the next method in the PagedAsyncIterableIterator interface to return a promise of IteratorResult<T> instead of IteratorResult<T, T>. The second parameter (the type of return expressions) will default to any. This is not a breaking change because it changes the type of return expressions from T to the less precise type any.

For context, IteratorResult was updated in v3.6 to take an extra type variable that specifies the type of return expressions. The change in this PR enables older versions of the typescript compiler (<3.6) to compile code that uses core-paging since they understand the IteratorResult<T> type only. This change is correct IMO because I never seen any return expressions in our iterators in the first place so promising the customers of all our iterators that there will always be a returned value of type T is incorrect.

I cannot think of a code that could break because of this change.

This PR also adds a simple sample that I used for testing.

@jeremymeng
Copy link
Member

Maybe @HarshaNalluru still remember the details? Setting both to T seems to offer a little help in the IDE: for both done cases we get intellisense for our value

type IteratorResult<T, TReturn = any> =
  | IteratorYieldResult<T>
  | IteratorReturnResult<TReturn>;
interface IteratorYieldResult<TYield> {
  done?: false;
  value: TYield;
}
interface IteratorReturnResult<TReturn> {
  done: true;
  value: TReturn;
}

@deyaaeldeen
Copy link
Member Author

Maybe @HarshaNalluru still remember the details? Setting both to T seems to offer a little help in the IDE: for both done cases we get intellisense for our value

Our implementation of next does not have return expressions so I am not sure what intellisense benefit do we get from setting the returned value type.

@HarshaNalluru
Copy link
Member

HarshaNalluru commented Dec 22, 2021

#10599 (comment) reference

Please go through the PR #10599

image
Try this sample(from storage-blob) to see how the IntelliSense varies with your change, for the version combinations suggested in the #10599 (comment) reference.

GIST - you'll lose the IntelliSense for "value" if I'm not wrong.

#10599 should have fixed the user issue I guess, but it was deleted in #16732 😔

@deyaaeldeen
Copy link
Member Author

I am not sure how storage implements their next but here is the intellisense experience for core-paging's next implementation:
yielded values (done = false) are typed as TElement
old_iter1
returned values (done = true) are typed as any but since there is no returned values anyway, it should not matter!
old_iter2

Comment on lines 41 to 42
const elementHasTypeAny: IsAny<typeof element> = false;
isFalse(elementHasTypeAny);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am adding these to make sure the type of element is not any. The first line is enough for this purpose, but I had to add the second to trick TS into thinking the variable elementHasTypeAny is "used".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe put a comment to this effect in the code?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could wrap this up into an empty assertNotAny function call maybe?

function assertNotAny<T extends (IsAny<T> extends true ? never : any)>(t: T): void { }

could work, assuming the intent is that the compiler yells at you if it is any for some reason. But I'm not sure this needs to be in the samples?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, couldn't it be a test?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bterlson nice, I used assertNotAny!

@xirzec Good point! I converted it to tests with descriptive names.

Copy link
Member

@xirzec xirzec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the rationale that we aren't going to be setting value when done is true, so this works perfectly.

If we wanted to be more precise, I think you could use IteratorResult<T, undefined> and doing some downlevel TS magic to change the definition under 3.6 to IteratorResult<T>, but I believe the fix in this PR is both sufficient and far simpler.

Comment on lines 41 to 42
const elementHasTypeAny: IsAny<typeof element> = false;
isFalse(elementHasTypeAny);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe put a comment to this effect in the code?

Copy link
Member

@witemple-msft witemple-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HarshaNalluru @deyaaeldeen I agree with Deya's reasoning that because we do not, in practice, return values from generators in core-paging (or in storage, from what I checked), that giving it the same type as the iterator's yield is wrong. We are promising the user that we will produce something we don't actually return.

When it comes to IteratorResult, the value is typed dependently of done. IteratorResult<T, T> says "this iterator produces value: T regardless of the state of done, but at runtime I don't think this is true, so I'm in agreement with the plan to remove the second type argument. Users will still get high-quality intellisense for value, but only in contexts where they've checked if the result is done. If they were getting itellisense for value without checking done, then that was incorrect. (For what it's worth @deyaaeldeen I think this is right regardless of TS3.4 compatibility or anything else, so if it gets us compatibility with an older TS version then that's just extras.)

@deyaaeldeen I'm not sure you get much value from having the sample as opposed to a test, but I do think there is room for a set of universal paging samples so that we don't have to write the same "hey look, you can iterate this thing by page" sample many times.


import { getPagedAsyncIterator, PagedResult, PageSettings } from "@azure/core-paging";

type IsAny<T> = boolean extends (T extends never ? true : false) ? true : false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite clever.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love seeing a novel any test 😁 I usually roll with 0 extends (1 & T) ? true : false.

@@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels more like a test than a sample.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I converted it to tests and kept only the for-await looping in the sample. Not entirely happy with the definition of the operation itself so any suggestions there are welcome.

@witemple-msft
Copy link
Member

IteratorResult<T, undefined>

Promising to return undefined might make the compiler unhappy if you don't explicitly write out return undefined;. This is how it behaves in function definitions, at least. I guess this begs the question of whether IteratorResult<T, void> is a sensible thing to write.

Copy link
Member

@bterlson bterlson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a couple non-essential comments, looks good.

@deyaaeldeen
Copy link
Member Author

Since there is some interest in downleveling IteratorResult in order to provide a better type experience in TS v >= 3.6, I went ahead and added it in sandersn/downlevel-dts#61.

@deyaaeldeen deyaaeldeen merged commit 1a77b3c into Azure:main Jan 5, 2022
@deyaaeldeen deyaaeldeen deleted the core-paging/fix-19394 branch January 5, 2022 00:35
azure-sdk pushed a commit to azure-sdk/azure-sdk-for-js that referenced this pull request Jun 24, 2022
Web ant97 2022 03 01 (Azure#19430)

* Adds base for updating Microsoft.Web from version stable/2021-03-01 to version 2022-03-01

* Updates readme

* Updates API version in new specs and examples

* Carry fwd Microsoft.CertificateRegistration and Microsoft.DomainRegis… (Azure#18460)

* Carry fwd Microsoft.CertificateRegistration and Microsoft.DomainRegistration RPs to Api-version 2022-03-01

* Add x-ms-enum for array of inline enums. Fix reference to older api version for common defs

* Add examples for App Service Certificate orders

* Add suppressions back

* Add examples for Microsoft.DomainRegistration RP

* Fixes from prettier

* Remove unused example

* Add Unhealthy CustomDomainStatus for StaticSites (Azure#18557)

* Adding VnetRouteAllEnabled, VnetImagePullEnabled, VnetContentShareEnabled site properties to CommonDefinitions (Azure#18627)

* Added vnet realted site properties to common definitions

* removed tab

Co-authored-by: Tanay Bhartia <[email protected]>

* Add Ftp/Remote debug properties to Ase network config (Azure#18549)

* Add Ftp/Remote debug properties to Ase network config

* Switched changes to the right file

* Add Networking config to hosting environment creates

* Added some of the missing examples

* Remove disallowed properties from example Ase GET response

* Adjusted responses from other gets

* Added a bunch more examples

* Added other missing examples for Ase.

* Added more missing examples for Ase.

* fixed formatting

* Edit some examples, add back privatelink apis and try to supress the errors for missing examples.

* Fix suppress spelling and fix prettier

* Added missing privatelink examples since suppressing did not work

* Add Bring your own Backend operations (Azure#18517)

* add bring your own backend operations

* add missing files

* fix json formattin

* validation fixes

* fix

* fix more validation

* fix ids

* fix model validation

* fix descriptions

* fix environmentName description

* change to 202

* revert change to 202

* customhostnamesites optional param for 2022 API version (Azure#18670)

* fix DUPLICATE_PARAMETER

* prettier

* fix  OBJECT_ADDITIONAL_PROPERTIES

* fix random sub in examples

* add optional hostname param to customHostnameSites RT and add examples

* move changes from 2021 api version to 2022

* move examples to 2022

* Revert "add optional hostname param to customHostnameSites RT and add examples"

This reverts commit 7fc318e190de2108222dbb869d9a6219a02cae2f.

* remove examples from 2021

* fix lintDiff

* prettier fix

* model validation fix

* suppress model validation in readme

Co-authored-by: Elle Tojaroon <[email protected]>

* [Microsoft.Web] Add CustomDnsSuffixConfiguration to ASE (Azure#18553)

* [Microsoft.Web] Add CustomDnsSuffixConfiguration to AppServiceEnvironments

* Add examples

* Fix style issues

* Add customdnssuffix to custom-words

* Add CustomDnsSuffixConfiguration to AppServiceEnvironment definition

* Address remaining model validation errors

* Adjusted networking configuration definitions

* Addressed Arm review feedback

Co-authored-by: Jarod Aerts <[email protected]>

* Fork/web ant97 2022 03 01 (Azure#19259)

* Added DeploymentStatus API specs and examples

* change status to enum, rm extra dot

* suppress missing examples for now

* rm id from List Deployment Slot examples

* rm location, and rename deploymentId in GetSiteDeploymentStatus examples

* change model as string to true, add 202 operations for DeploymentStatus ops to spec

* rename operationId to deploymentStatusId, undo deploymentId rename

* add long running op, as per linter

Co-authored-by: Shubham Dhond <[email protected]>
Co-authored-by: Weidong Xu <[email protected]>

* PublicNetworkAccess Swagger Change (Azure#19352)

* add publicNetworkAccess property in site for swagger

* add suppression to fix Model Validation CI

* Add allowed values in the description

* Add specs for Hosting Environment Maintenance Control (Azure#18691)

* Add documentation for Hosting Environment Maintenance Control

* Update example file name

* Make UpgradePreference non-nullable

* Correct indentation to 2 spaces

* Fix upgradePreference indentation

* Address swagger feedback. Add enums with descriptions.

* Move testNotification to request body. Update description text

* Fix testNotification

* Fix example

* Replace TestNotification parameter with TestUpgradeNotification API

* Rename to TestUpgradeAvailableNotification

* Added logic apps operations (Azure#18604)

* Added Initial Operations and some definitions

* Adding additional objects to support Logic Apps operations

* Updated path and parameters to match testing

* Prettier Fixes

* oav validat-example fixes

* Github validation fixes

* Prettier

* Updated examples and paths

* Prettier

* Revert "Prettier"

This reverts commit da090adf8005e0bd6fdc2ddebbd4390e5c4b6b20.

* Prettier

* Fixed model validation errors

* Filled in nextLinkName values

* Fixing swagger lintdiff errors

* added x-ms-long-running to async calls

* Adds numberOfWorkers to app service plan (Azure#19475)

* Adds numberOfWorkers to app service plan

* Add suppression for missing ASP examples

* Updating to fix workflow errors (Azure#19490)

* Removed conflicting resource definition and added reference to CommonDefinitions.json (Azure#19507)

* Removed conflicting resource definition and added reference to CommonDefinitions.json

* Updated resource to have workflow version

* Updated nextLink object to be correctly tagged (Azure#19560)

* Updated nextLink object to be correctly tagged

* Adding value placeholder

* Updated SKU to remove conflict

* Updated x-ms-enum

* Added missing model

* Adding more missing models

* Add suppressions for missing examples (Azure#19563)

Co-authored-by: Joseph Lin <[email protected]>
Co-authored-by: Tanay Bhartia <[email protected]>
Co-authored-by: Tanay Bhartia <[email protected]>
Co-authored-by: JarodAertsMs <[email protected]>
Co-authored-by: annikel <[email protected]>
Co-authored-by: Paviya (Elle) Tojaroon <[email protected]>
Co-authored-by: Elle Tojaroon <[email protected]>
Co-authored-by: Chris Chen <[email protected]>
Co-authored-by: Jarod Aerts <[email protected]>
Co-authored-by: edwin-msft <[email protected]>
Co-authored-by: Shubham Dhond <[email protected]>
Co-authored-by: Weidong Xu <[email protected]>
Co-authored-by: jiansong-msft <[email protected]>
Co-authored-by: Derek Johnson <[email protected]>
Co-authored-by: Alex Karcher <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants