Skip to content

Commit 1163091

Browse files
authored
Merge 50fb6ee into 0508333
2 parents 0508333 + 50fb6ee commit 1163091

File tree

7 files changed

+440
-3
lines changed

7 files changed

+440
-3
lines changed

src/Components/test/E2ETest/Tests/InteropTest.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ public void CanInvokeInteropMethods()
108108
["invokeNewWithClassConstructorAsync.function"] = "6",
109109
["invokeNewWithNonConstructorAsync"] = "Success",
110110
// Function reference tests
111-
["changeFunctionViaObjectReferenceAsync"] = "42"
111+
["changeFunctionViaObjectReferenceAsync"] = "42",
112+
["invokeDelegateFromAsAsyncFunction"] = "42"
112113
};
113114

114115
var expectedSyncValues = new Dictionary<string, string>

src/Components/test/testassets/BasicTestApp/InteropComponent.razor

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,9 @@
614614
var testClassRef = await JSRuntime.InvokeNewAsync("jsInteropTests.TestClass", "abraka");
615615
await testClassRef.SetValueAsync("getTextLength", funcRef);
616616
ReturnValues["changeFunctionViaObjectReferenceAsync"] = (await testClassRef.InvokeAsync<int>("getTextLength")).ToString();
617+
618+
var funcDelegate = funcRef.AsAsyncFunction<Func<Task<int>>>();
619+
ReturnValues["invokeDelegateFromAsAsyncFunction"] = (await funcDelegate()).ToString();
617620
}
618621

619622
private void FunctionReferenceTests()

src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,17 @@ export module DotNet {
562562
const targetInstance = cachedJSObjectsById[targetInstanceId];
563563

564564
if (targetInstance) {
565-
return targetInstance.resolveInvocationHandler(identifier, callType ?? JSCallType.FunctionCall);
565+
if (identifier) {
566+
return targetInstance.resolveInvocationHandler(identifier, callType ?? JSCallType.FunctionCall);
567+
} else {
568+
const wrappedObject = targetInstance.getWrappedObject();
569+
570+
if (wrappedObject instanceof Function) {
571+
return wrappedObject;
572+
} else {
573+
throw new Error(`JS object instance with ID ${targetInstanceId} is not a function.`);
574+
}
575+
}
566576
}
567577

568578
throw new Error(`JS object instance with ID ${targetInstanceId} does not exist (has it been disposed?).`);
@@ -626,7 +636,10 @@ export module DotNet {
626636
if (!isReadableProperty(parent, memberName)) {
627637
throw new Error(`The property '${identifier}' is not defined or is not readable.`);
628638
}
629-
return () => parent[memberName];
639+
640+
return parent[memberName] instanceof Function
641+
? () => parent[memberName].bind(parent)
642+
: () => parent[memberName];
630643
case JSCallType.SetValue:
631644
if (!isWritableProperty(parent, memberName)) {
632645
throw new Error(`The property '${identifier}' is not writable.`);
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Concurrent;
5+
using System.Diagnostics.CodeAnalysis;
6+
using System.Reflection;
7+
using static Microsoft.AspNetCore.Internal.LinkerFlags;
8+
9+
namespace Microsoft.JSInterop.Infrastructure;
10+
11+
/// <summary>
12+
/// Helper for constructing a Func delegate that wraps interop call to a JavaScript function referenced via <see cref="IJSObjectReference"/>.
13+
/// </summary>
14+
internal readonly struct JSFunctionReference
15+
{
16+
/// <summary>
17+
/// Caches previously constructed MethodInfo instances for various delegate types.
18+
/// </summary>
19+
private static readonly ConcurrentDictionary<Type, MethodInfo> _methodInfoCache = new();
20+
21+
private readonly IJSObjectReference _jsObjectReference;
22+
23+
public JSFunctionReference(IJSObjectReference jsObjectReference)
24+
{
25+
_jsObjectReference = jsObjectReference;
26+
}
27+
28+
public static T CreateInvocationDelegate<T>(IJSObjectReference jsObjectReference) where T : Delegate
29+
{
30+
Type delegateType = typeof(T);
31+
32+
if (_methodInfoCache.TryGetValue(delegateType, out var wrapperMethod))
33+
{
34+
var wrapper = new JSFunctionReference(jsObjectReference);
35+
return (T)Delegate.CreateDelegate(delegateType, wrapper, wrapperMethod);
36+
}
37+
38+
if (!delegateType.IsGenericType)
39+
{
40+
throw CreateInvalidTypeParameterException(delegateType);
41+
}
42+
43+
var returnTypeCandidate = delegateType.GenericTypeArguments[^1];
44+
45+
if (returnTypeCandidate == typeof(ValueTask))
46+
{
47+
var methodName = GetVoidMethodName(delegateType);
48+
return CreateVoidDelegate<T>(delegateType, jsObjectReference, methodName);
49+
}
50+
else if (returnTypeCandidate == typeof(Task))
51+
{
52+
var methodName = GetVoidTaskMethodName(delegateType);
53+
return CreateVoidDelegate<T>(delegateType, jsObjectReference, methodName);
54+
}
55+
else if (returnTypeCandidate.IsGenericType)
56+
{
57+
var returnTypeGenericTypeDefinition = returnTypeCandidate.GetGenericTypeDefinition();
58+
59+
if (returnTypeGenericTypeDefinition == typeof(ValueTask<>))
60+
{
61+
var methodName = GetMethodName(delegateType);
62+
var innerReturnType = returnTypeCandidate.GenericTypeArguments[0];
63+
return CreateDelegate<T>(delegateType, innerReturnType, jsObjectReference, methodName);
64+
}
65+
66+
else if (returnTypeGenericTypeDefinition == typeof(Task<>))
67+
{
68+
var methodName = GetTaskMethodName(delegateType);
69+
var innerReturnType = returnTypeCandidate.GenericTypeArguments[0];
70+
return CreateDelegate<T>(delegateType, innerReturnType, jsObjectReference, methodName);
71+
}
72+
}
73+
74+
throw CreateInvalidTypeParameterException(delegateType);
75+
}
76+
77+
private static T CreateDelegate<T>(Type delegateType, Type returnType, IJSObjectReference jsObjectReference, string methodName) where T : Delegate
78+
{
79+
var wrapperMethod = typeof(JSFunctionReference).GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance)!;
80+
Type[] genericArguments = [.. delegateType.GenericTypeArguments[..^1], returnType];
81+
82+
#pragma warning disable IL2060 // Call to 'System.Reflection.MethodInfo.MakeGenericMethod' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method.
83+
var concreteWrapperMethod = wrapperMethod.MakeGenericMethod(genericArguments);
84+
#pragma warning restore IL2060 // Call to 'System.Reflection.MethodInfo.MakeGenericMethod' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method.
85+
86+
_methodInfoCache.TryAdd(delegateType, concreteWrapperMethod);
87+
88+
var wrapper = new JSFunctionReference(jsObjectReference);
89+
return (T)Delegate.CreateDelegate(delegateType, wrapper, concreteWrapperMethod);
90+
}
91+
92+
private static T CreateVoidDelegate<T>(Type delegateType, IJSObjectReference jsObjectReference, string methodName) where T : Delegate
93+
{
94+
var wrapperMethod = typeof(JSFunctionReference).GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance)!;
95+
Type[] genericArguments = delegateType.GenericTypeArguments[..^1];
96+
97+
#pragma warning disable IL2060 // Call to 'System.Reflection.MethodInfo.MakeGenericMethod' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method.
98+
var concreteWrapperMethod = wrapperMethod.MakeGenericMethod(genericArguments);
99+
#pragma warning restore IL2060 // Call to 'System.Reflection.MethodInfo.MakeGenericMethod' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method.
100+
101+
_methodInfoCache.TryAdd(delegateType, concreteWrapperMethod);
102+
103+
var wrapper = new JSFunctionReference(jsObjectReference);
104+
return (T)Delegate.CreateDelegate(delegateType, wrapper, concreteWrapperMethod);
105+
}
106+
107+
private static InvalidOperationException CreateInvalidTypeParameterException(Type delegateType)
108+
{
109+
return new InvalidOperationException(
110+
$"The type {delegateType} is not supported as the type parameter of '{nameof(JSObjectReferenceExtensions.AsAsyncFunction)}'. 'T' must be Func with the return type Task<TResult> or ValueTask<TResult>.");
111+
}
112+
113+
private static string GetMethodName(Type delegateType) => delegateType.GetGenericTypeDefinition() switch
114+
{
115+
var gd when gd == typeof(Func<>) => nameof(Invoke0),
116+
var gd when gd == typeof(Func<,>) => nameof(Invoke1),
117+
var gd when gd == typeof(Func<,,>) => nameof(Invoke2),
118+
var gd when gd == typeof(Func<,,,>) => nameof(Invoke3),
119+
var gd when gd == typeof(Func<,,,,>) => nameof(Invoke4),
120+
var gd when gd == typeof(Func<,,,,,>) => nameof(Invoke5),
121+
var gd when gd == typeof(Func<,,,,,,>) => nameof(Invoke6),
122+
var gd when gd == typeof(Func<,,,,,,,>) => nameof(Invoke7),
123+
var gd when gd == typeof(Func<,,,,,,,,>) => nameof(Invoke8),
124+
_ => throw CreateInvalidTypeParameterException(delegateType)
125+
};
126+
127+
private static string GetTaskMethodName(Type delegateType) => delegateType.GetGenericTypeDefinition() switch
128+
{
129+
var gd when gd == typeof(Func<>) => nameof(InvokeTask0),
130+
var gd when gd == typeof(Func<,>) => nameof(InvokeTask1),
131+
var gd when gd == typeof(Func<,,>) => nameof(InvokeTask2),
132+
var gd when gd == typeof(Func<,,,>) => nameof(InvokeTask3),
133+
var gd when gd == typeof(Func<,,,,>) => nameof(InvokeTask4),
134+
var gd when gd == typeof(Func<,,,,,>) => nameof(InvokeTask5),
135+
var gd when gd == typeof(Func<,,,,,,>) => nameof(InvokeTask6),
136+
var gd when gd == typeof(Func<,,,,,,,>) => nameof(InvokeTask7),
137+
var gd when gd == typeof(Func<,,,,,,,,>) => nameof(InvokeTask8),
138+
_ => throw CreateInvalidTypeParameterException(delegateType)
139+
};
140+
141+
private static string GetVoidMethodName(Type delegateType) => delegateType.GetGenericTypeDefinition() switch
142+
{
143+
var gd when gd == typeof(Func<>) => nameof(InvokeVoid0),
144+
var gd when gd == typeof(Func<,>) => nameof(InvokeVoid1),
145+
var gd when gd == typeof(Func<,,>) => nameof(InvokeVoid2),
146+
var gd when gd == typeof(Func<,,,>) => nameof(InvokeVoid3),
147+
var gd when gd == typeof(Func<,,,,>) => nameof(InvokeVoid4),
148+
var gd when gd == typeof(Func<,,,,,>) => nameof(InvokeVoid5),
149+
var gd when gd == typeof(Func<,,,,,,>) => nameof(InvokeVoid6),
150+
var gd when gd == typeof(Func<,,,,,,,>) => nameof(InvokeVoid7),
151+
var gd when gd == typeof(Func<,,,,,,,,>) => nameof(InvokeVoid8),
152+
_ => throw CreateInvalidTypeParameterException(delegateType)
153+
};
154+
155+
private static string GetVoidTaskMethodName(Type delegateType) => delegateType.GetGenericTypeDefinition() switch
156+
{
157+
var gd when gd == typeof(Func<>) => nameof(InvokeVoidTask0),
158+
var gd when gd == typeof(Func<,>) => nameof(InvokeVoidTask1),
159+
var gd when gd == typeof(Func<,,>) => nameof(InvokeVoidTask2),
160+
var gd when gd == typeof(Func<,,,>) => nameof(InvokeVoidTask3),
161+
var gd when gd == typeof(Func<,,,,>) => nameof(InvokeVoidTask4),
162+
var gd when gd == typeof(Func<,,,,,>) => nameof(InvokeVoidTask5),
163+
var gd when gd == typeof(Func<,,,,,,>) => nameof(InvokeVoidTask6),
164+
var gd when gd == typeof(Func<,,,,,,,>) => nameof(InvokeVoidTask7),
165+
var gd when gd == typeof(Func<,,,,,,,,>) => nameof(InvokeVoidTask8),
166+
_ => throw CreateInvalidTypeParameterException(delegateType)
167+
};
168+
169+
// Variants returning ValueTask<T> using InvokeAsync
170+
public ValueTask<TResult> Invoke0<[DynamicallyAccessedMembers(JsonSerialized)] TResult>() => _jsObjectReference.InvokeAsync<TResult>(string.Empty, []);
171+
public ValueTask<TResult> Invoke1<T1, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1]);
172+
public ValueTask<TResult> Invoke2<T1, T2, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2]);
173+
public ValueTask<TResult> Invoke3<T1, T2, T3, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3]);
174+
public ValueTask<TResult> Invoke4<T1, T2, T3, T4, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3, arg4]);
175+
public ValueTask<TResult> Invoke5<T1, T2, T3, T4, T5, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3, arg4, arg5]);
176+
public ValueTask<TResult> Invoke6<T1, T2, T3, T4, T5, T6, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6]);
177+
public ValueTask<TResult> Invoke7<T1, T2, T3, T4, T5, T6, T7, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6, arg7]);
178+
public ValueTask<TResult> Invoke8<T1, T2, T3, T4, T5, T6, T7, T8, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8]);
179+
180+
// Variants returning ValueTask using InvokeVoidAsync
181+
public ValueTask InvokeVoid0() => _jsObjectReference.InvokeVoidAsync(string.Empty);
182+
public ValueTask InvokeVoid1<T1>(T1 arg1) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1]);
183+
public ValueTask InvokeVoid2<T1, T2>(T1 arg1, T2 arg2) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2]);
184+
public ValueTask InvokeVoid3<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3]);
185+
public ValueTask InvokeVoid4<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3, arg4]);
186+
public ValueTask InvokeVoid5<T1, T2, T3, T4, T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3, arg4, arg5]);
187+
public ValueTask InvokeVoid6<T1, T2, T3, T4, T5, T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6]);
188+
public ValueTask InvokeVoid7<T1, T2, T3, T4, T5, T6, T7>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6, arg7]);
189+
public ValueTask InvokeVoid8<T1, T2, T3, T4, T5, T6, T7, T8>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8]);
190+
191+
// Variants returning Task<T> using InvokeAsync
192+
public Task<TResult> InvokeTask0<[DynamicallyAccessedMembers(JsonSerialized)] TResult>() => _jsObjectReference.InvokeAsync<TResult>(string.Empty, []).AsTask();
193+
public Task<TResult> InvokeTask1<T1, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1]).AsTask();
194+
public Task<TResult> InvokeTask2<T1, T2, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2]).AsTask();
195+
public Task<TResult> InvokeTask3<T1, T2, T3, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3]).AsTask();
196+
public Task<TResult> InvokeTask4<T1, T2, T3, T4, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3, arg4]).AsTask();
197+
public Task<TResult> InvokeTask5<T1, T2, T3, T4, T5, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3, arg4, arg5]).AsTask();
198+
public Task<TResult> InvokeTask6<T1, T2, T3, T4, T5, T6, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6]).AsTask();
199+
public Task<TResult> InvokeTask7<T1, T2, T3, T4, T5, T6, T7, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6, arg7]).AsTask();
200+
public Task<TResult> InvokeTask8<T1, T2, T3, T4, T5, T6, T7, T8, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) => _jsObjectReference.InvokeAsync<TResult>(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8]).AsTask();
201+
202+
// Variants returning Task using InvokeVoidAsync
203+
public Task InvokeVoidTask0() => _jsObjectReference.InvokeVoidAsync(string.Empty).AsTask();
204+
public Task InvokeVoidTask1<T1>(T1 arg1) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1]).AsTask();
205+
public Task InvokeVoidTask2<T1, T2>(T1 arg1, T2 arg2) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2]).AsTask();
206+
public Task InvokeVoidTask3<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3]).AsTask();
207+
public Task InvokeVoidTask4<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3, arg4]).AsTask();
208+
public Task InvokeVoidTask5<T1, T2, T3, T4, T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3, arg4, arg5]).AsTask();
209+
public Task InvokeVoidTask6<T1, T2, T3, T4, T5, T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6]).AsTask();
210+
public Task InvokeVoidTask7<T1, T2, T3, T4, T5, T6, T7>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6, arg7]).AsTask();
211+
public Task InvokeVoidTask8<T1, T2, T3, T4, T5, T6, T7, T8>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) => _jsObjectReference.InvokeVoidAsync(string.Empty, [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8]).AsTask();
212+
}

src/JSInterop/Microsoft.JSInterop/src/JSObjectReferenceExtensions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,19 @@ public static ValueTask<IJSObjectReference> InvokeNewAsync(this IJSObjectReferen
167167

168168
return jsObjectReference.InvokeNewAsync(identifier, cancellationToken, args);
169169
}
170+
171+
/// <summary>
172+
/// Converts a JavaScript function reference into a .NET delegate of the specified type.
173+
/// </summary>
174+
/// <typeparam name="T">The type of the delegate to create. Must be a Func with the result type <see cref="Task"/>, <see cref="Task{R}"/>, <see cref="ValueTask"/>, or <see cref="ValueTask{R}"/>.</typeparam>
175+
/// <param name="jsObjectReference">The JavaScript object reference that represents the function to be invoked.</param>
176+
/// <returns>A Func delegate of type <typeparamref name="T"/> that can be used to invoke the JavaScript function.</returns>
177+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="jsObjectReference"/> is null.</exception>
178+
/// <exception cref="InvalidOperationException">Thrown when <typeparamref name="T"/> is not a valid Func type.</exception>
179+
public static T AsAsyncFunction<T>(this IJSObjectReference jsObjectReference) where T : Delegate
180+
{
181+
ArgumentNullException.ThrowIfNull(jsObjectReference);
182+
183+
return JSFunctionReference.CreateInvocationDelegate<T>(jsObjectReference);
184+
}
170185
}

0 commit comments

Comments
 (0)