mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-26 06:34:20 +08:00
Return undefined from overload calls whose generic body misses an index
A call that selects an overload signature of a generic function now gives its instance the same result type as an implementation-signature call: when the body can return an indexed read that misses, the result includes undefined, as in JavaScript. Before, such a call failed at the missing read instead of returning undefined.
This commit is contained in:
@@ -303,14 +303,7 @@ export function genericCallInstance(
|
||||
fenceGenericSignatureResult(lowerer, expr, retTs);
|
||||
lowerer.badType(expr, retTs);
|
||||
}
|
||||
// A body that can return an indexed read that misses returns undefined
|
||||
// there, as in JavaScript, whatever the declared result says. An async
|
||||
// body's promise is always present and settles with undefined there.
|
||||
const returnType = lowerer.runtimeOptionalGenericReturns.has(info.decl)
|
||||
? mappedReturn.kind === "promise"
|
||||
? { kind: "promise" as const, inner: lowerer.runtimeOptionalType(mappedReturn.inner) }
|
||||
: lowerer.runtimeOptionalType(mappedReturn)
|
||||
: mappedReturn;
|
||||
const returnType = runtimeReturnType(lowerer, info, mappedReturn);
|
||||
|
||||
// keyof-constrained type parameters (`K extends keyof T`): the bound
|
||||
// LITERAL is semantic — the instance body reads the named field — so
|
||||
@@ -417,7 +410,8 @@ function overloadCallInstance(
|
||||
if (!returnType) return null;
|
||||
// keyof-constrained parameters key their instance on the bound literal,
|
||||
// as on the implementation-signature path.
|
||||
return internGenericInstance(lowerer, expr, info, params, returnType, () => bindings, {
|
||||
const runtimeReturn = runtimeReturnType(lowerer, info, returnType);
|
||||
return internGenericInstance(lowerer, expr, info, params, runtimeReturn, () => bindings, {
|
||||
...(keyofConstrainedTypeParams(info).size > 0
|
||||
? { extraKey: keyofLiteralKey(info, tsBindings) }
|
||||
: {}),
|
||||
@@ -425,6 +419,17 @@ function overloadCallInstance(
|
||||
});
|
||||
}
|
||||
|
||||
/** An instance's result type: a body that can return an indexed read
|
||||
* that misses returns undefined there, as in JavaScript, whatever the
|
||||
* declared result says. An async body's promise is always present and
|
||||
* settles with undefined there. */
|
||||
function runtimeReturnType(lowerer: Lowerer, info: GenericFnInfo, mapped: IrType): IrType {
|
||||
if (!lowerer.runtimeOptionalGenericReturns.has(info.decl)) return mapped;
|
||||
return mapped.kind === "promise"
|
||||
? { kind: "promise", inner: lowerer.runtimeOptionalType(mapped.inner) }
|
||||
: lowerer.runtimeOptionalType(mapped);
|
||||
}
|
||||
|
||||
/** A generic factory whose result is one of its own local classes
|
||||
* (`function base<T>(v: T) { return class { value: T = v }; }`). Each
|
||||
* instance has its own class, so the instance is keyed on its bindings and
|
||||
|
||||
@@ -32,3 +32,16 @@ function read<T, K extends keyof T>(row: T, key: K, fallback?: T[K]): T[K] {
|
||||
const row: Row = { a: 1, b: 2, name: "r" };
|
||||
console.log(read(row, "a"), read(row, "b"), read(row, "name"));
|
||||
console.log(read(row, "b", 0) + read(row, "a", 0));
|
||||
|
||||
// An overload call whose implementation returns an indexed read that
|
||||
// misses returns undefined, as the implementation's own calls do.
|
||||
function at<T>(items: T[], index: number): T;
|
||||
function at<T>(items: T[], index: number, label: string): T;
|
||||
function at<T>(items: T[], index: number, label?: string): T {
|
||||
if (label !== undefined) console.log(label);
|
||||
return items[index];
|
||||
}
|
||||
console.log(at([1, 2, 3], 1), at([1, 2, 3], 5));
|
||||
console.log(at(["a", "b"], 0, "strings"), at(["a", "b"], 9, "strings"));
|
||||
const missing = at([{ id: 1 }], 3);
|
||||
console.log(missing === undefined, typeof missing);
|
||||
|
||||
Reference in New Issue
Block a user