Skip to content

[core-tracing] Fix the return type of withSpan#20103

Merged
maorleger merged 5 commits intoAzure:mainfrom
maorleger:fix-typeof-withSpan
Jan 28, 2022
Merged

[core-tracing] Fix the return type of withSpan#20103
maorleger merged 5 commits intoAzure:mainfrom
maorleger:fix-typeof-withSpan

Conversation

@maorleger
Copy link
Copy Markdown
Member

Packages impacted by this PR

@azure/core-tracing

Issues associated with this PR

N/A - created after discussion with some team members

Describe the problem that is addressed by this PR

withSpan is meant to await some user-provided callback and set the appropriate
status on the created span before closing it. Because we have no way of knowing
whether the callback is sync or async we must promisify it first. Up until now that
meant that the return type of a promise-returning callback was
Promise<Promise<...>> which does not reflect the reality that await will
recursively unwrap promises. That meant that the consumer must also either
await or mark the function as async in order to avoid a return-type
mismatch.

What are the possible designs available to address the problem? If there are more than one possible design, why was the one in this PR chosen?

The solution here is to introduce an AwaitedLike type which is a narrower
version of TypeScript 4.5's Awaited type. Because our min-bar is lower we
cannot take advantage of this new type yet, so I introduced something like it
which can be replaced with the broader type once we support TS >= 4.5.

Are there test cases added in this PR? (If not, why?)

We already have test cases for this function for both sync and async callbacks,
as this is a pure type-change with no runtime behavior change no test cases are
needed

Checklists

  • Added impacted package name to the issue description
  • Does this PR needs any fixes in the SDK Generator?** (If so, create an Issue in the Autorest/typescript repository and link it here)
  • Added a changelog (if necessary)

);
span.setStatus({ status: "success" });
return result;
return result as ReturnType<typeof withSpan>;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is not needed in TS 4.4 see playground link but for reasons unknown to me TS4.2 infers the returntype of this as unknown

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for sharing the playground!

@azure-sdk
Copy link
Copy Markdown
Collaborator

API changes have been detected in @azure/core-tracing. You can review API changes here

API changes

+ export declare type AwaitedLike<T> = T extends object & {
+     then(onfulfilled: infer F): any;
+ } ? F extends (value: infer V) => any ? AwaitedLike<V> : never : T;
-         }, Callback extends (updatedOptions: Options, span: Omit<TracingSpan, "end">) => ReturnType<Callback>>(name: string, operationOptions: Options, callback: Callback, spanOptions?: TracingSpanOptions): Promise<ReturnType<Callback>>;
+         }, Callback extends (updatedOptions: Options, span: Omit<TracingSpan, "end">) => ReturnType<Callback>>(name: string, operationOptions: Options, callback: Callback, spanOptions?: TracingSpanOptions): Promise<AwaitedLike<ReturnType<Callback>>>;

Comment thread sdk/core/core-tracing/src/interfaces.ts Outdated
* unwraps the "awaited type", emulating the behavior of `await`.
*/
// eslint-disable-next-line @typescript-eslint/ban-types -- I want to stay consistent with TypeScript 4.5's Awaited type
export type AwaitedLike<T> = T extends object & { then(onfulfilled: infer F): any } // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These comments are great! Thanks for documenting this type 😄

Copy link
Copy Markdown
Member

@joheredi joheredi left a comment

Choose a reason for hiding this comment

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

Looks good! Do we expect this to be a breaking change?

@maorleger
Copy link
Copy Markdown
Member Author

Looks good! Do we expect this to be a breaking change?

In this case we're OK because these are all new and unreleased APIs 👍

Copy link
Copy Markdown
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.

LGTM but with a couple of notes about how the type is defined.

```ts

// @public
export type AwaitedLike<T> = T extends object & {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

AwaitedLike is somewhat strange as a name since it makes it sound like an interface for something that is "like an Awaited instance." Maybe something like UnwrapThenable if you really don't want to overlap with TS4.5's Awaited name.

Since tracing is in beta we can remove this in the future if we decide that we can rely on TS4.5's definition.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also, since { then: ... } is a subtype of object, there shouldn't be any difference between object & { then: ... } and { then: ... }.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Also, since { then: ... } is a subtype of object, there shouldn't be any difference between object & { then: ... } and { then: ... }.

Thanks, yeah good call!

As far as the name... maybe AwaitedThenable? I like having Awaited in the name what do you think?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I was chatting with Jeff and we went with Resolved<T> which feels appropriate here where the returntype is:

Promise<Resolved<ReturnType<Callback>>>;

@azure-sdk
Copy link
Copy Markdown
Collaborator

API changes have been detected in @azure/core-tracing. You can review API changes here

API changes

+ export declare type AwaitedLike<T> = T extends {
+     then(onfulfilled: infer F): any;
+ } ? F extends (value: infer V) => any ? AwaitedLike<V> : never : T;
-         }, Callback extends (updatedOptions: Options, span: Omit<TracingSpan, "end">) => ReturnType<Callback>>(name: string, operationOptions: Options, callback: Callback, spanOptions?: TracingSpanOptions): Promise<ReturnType<Callback>>;
+         }, Callback extends (updatedOptions: Options, span: Omit<TracingSpan, "end">) => ReturnType<Callback>>(name: string, operationOptions: Options, callback: Callback, spanOptions?: TracingSpanOptions): Promise<AwaitedLike<ReturnType<Callback>>>;

@azure-sdk
Copy link
Copy Markdown
Collaborator

API changes have been detected in @azure/core-tracing. You can review API changes here

API changes

+ export declare type Resolved<T> = T extends {
+     then(onfulfilled: infer F): any;
+ } ? F extends (value: infer V) => any ? Resolved<V> : never : T;
-         }, Callback extends (updatedOptions: Options, span: Omit<TracingSpan, "end">) => ReturnType<Callback>>(name: string, operationOptions: Options, callback: Callback, spanOptions?: TracingSpanOptions): Promise<ReturnType<Callback>>;
+         }, Callback extends (updatedOptions: Options, span: Omit<TracingSpan, "end">) => ReturnType<Callback>>(name: string, operationOptions: Options, callback: Callback, spanOptions?: TracingSpanOptions): Promise<Resolved<ReturnType<Callback>>>;

@maorleger maorleger merged commit 678b094 into Azure:main Jan 28, 2022
@maorleger maorleger deleted the fix-typeof-withSpan branch January 28, 2022 20:10
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.

4 participants