mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Give an async object-literal method its contextual return type
An async method in an object literal with no return annotation and a contextual signature inferred its own (narrower) literal type, while its `return` literal was built at the contextually typed record: the return statement then had to convert the wider record into the narrower literal, which fails with SC2002. The method now takes its return type from the contextual property's call signature, as the arrow form does. Sync methods are unchanged; they already adopt their slot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9eaa210e1f
commit
2acd199fbe
@@ -164,7 +164,33 @@ export function declaredReturnType(
|
||||
if (isJsSourceFile(ts.getSourceFileOfNode(decl)) && ts.isGetAccessorDeclaration(decl)) return DYN;
|
||||
const sig = lowerer.checker.getSignatureFromDeclaration(decl);
|
||||
if (!sig) lowerer.unsupported("SC1090", decl, "this function form");
|
||||
const retTsType = lowerer.checker.getReturnTypeOfSignature(sig);
|
||||
let retTsType = lowerer.checker.getReturnTypeOfSignature(sig);
|
||||
// An ASYNC object-literal method with no return annotation and a
|
||||
// contextual signature takes its return from that signature, as an async
|
||||
// arrow does: the body's `return` builds at the contextually typed
|
||||
// literal, so the checker's own inferred literal (narrower than the
|
||||
// contextual record) could never hold it.
|
||||
if (
|
||||
ts.isMethodDeclaration(decl) &&
|
||||
ts.isObjectLiteralExpression(decl.parent) &&
|
||||
decl.type === undefined &&
|
||||
decl.modifiers?.nodes.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) === true
|
||||
) {
|
||||
const contextual = lowerer.checker.getContextualType(decl.parent);
|
||||
const slot =
|
||||
contextual === undefined || (!ts.isIdentifier(decl.name) && !ts.isStringLiteral(decl.name))
|
||||
? undefined
|
||||
: lowerer.checker.getPropertyOfType(
|
||||
lowerer.checker.getNonNullableType(contextual),
|
||||
decl.name.text,
|
||||
);
|
||||
const signatures =
|
||||
slot === undefined
|
||||
? []
|
||||
: lowerer.checker.getCallSignatures(lowerer.checker.getTypeOfSymbol(slot));
|
||||
if (signatures.length === 1)
|
||||
retTsType = lowerer.checker.getReturnTypeOfSignature(signatures[0]!);
|
||||
}
|
||||
// An inferred null/undefined return in JS can read a mutable checked-value
|
||||
// field. Keep the actual value instead of restricting it to the initializer.
|
||||
if (
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/async-object-method-contextual-return.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
type Text = { type: "text"; text: string; textSignature?: string };
|
||||
type Image = { type: "image"; data: string; mimeType: string };
|
||||
interface ToolResult<D> { content: (Text | Image)[]; details: D; isError?: boolean }
|
||||
interface ToolDefinition<D> { name: string; execute(id: string, q: string): Promise<ToolResult<D>> }
|
||||
function define(): ToolDefinition<{ loaded: string[] }> {
|
||||
return {
|
||||
name: "search",
|
||||
async execute(_id, q) {
|
||||
const text = `found ${q}`;
|
||||
return { content: [{ type: "text", text }], details: { loaded: [q] } };
|
||||
},
|
||||
};
|
||||
}
|
||||
define().execute("1", "x").then((r) => console.log(JSON.stringify(r)));
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 3268, "san": 6404 }
|
||||
Reference in New Issue
Block a user