Index strings through checker-any receivers (#1965)

This commit is contained in:
Chris Tate
2026-10-23 02:44:28 -05:00
committed by GitHub
8 changed files with 47 additions and 2 deletions
@@ -566,8 +566,8 @@ export function lowerElementAccess(lowerer: Lowerer, expr: ts.ElementAccessExpre
}
}
if (receiverIr?.kind !== "array") {
const receiver = tryLowerExpression(lowerer, expr.expression);
{
const receiver = tryLowerExpression(lowerer, expr.expression);
if (
receiver?.type.kind === "dyn" ||
(receiver &&
@@ -618,7 +618,13 @@ export function lowerElementAccess(lowerer: Lowerer, expr: ts.ElementAccessExpre
}
}
}
if (receiverIr?.kind === "string") {
// A checker-`any` receiver that lowers to a string (an implicit-any
// package parameter specialized for a string argument) indexes the
// string the same way.
if (
receiverIr?.kind === "string" ||
(receiverIr === null && receiver?.type.kind === "string")
) {
// An indexed string property is absent for non-integer and missing
// indexes. charAt has different coercion and out-of-range semantics.
const recv = lowerer.lowerExpr(expr.expression);
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/7731-npm-any-string-index/main.ts"
],
"diags": []
}
@@ -0,0 +1,9 @@
// An implicit-any package function specialized for a string argument indexes
// the string (`base?.[0]`), as hono's mergePath does.
import { App, mergePath } from "merge-path";
console.log(new App().request("x"));
console.log(new App().request(new URL("http://h/p")));
console.log(new App().basePath("/api").basePath("v1/")._basePath);
console.log(mergePath("/api/", "/"), mergePath("a", "b", "/c"));
console.log(new App().basePath("").basePath("").request(""));
console.log(mergePath("", "x"), mergePath("/", "/"), mergePath("//", "y/"));
@@ -0,0 +1,2 @@
export declare const mergePath: (...paths: string[]) => string;
export declare class App { _basePath: string; request: (input: string | URL) => string; basePath(path: string): App; }
@@ -0,0 +1,10 @@
import { mergePath } from "./url.js";
class App {
_basePath = "/";
request = (input) => {
input = input.toString();
return mergePath("/", input);
};
basePath(path) { const s = new App(); s._basePath = mergePath(this._basePath, path); return s; }
}
export { App, mergePath };
@@ -0,0 +1 @@
{ "name": "merge-path", "version": "1.0.0", "type": "module", "main": "index.js", "types": "index.d.ts" }
+10
View File
@@ -0,0 +1,10 @@
/**
* Merge paths.
* @param {string[]} ...paths - The paths to merge.
* @returns {string} The merged path.
*/
const mergePath = (base, sub, ...rest) => {
if (rest.length) sub = mergePath(sub, ...rest);
return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.[base.length - 1] === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`;
};
export { mergePath };
@@ -0,0 +1 @@
{ "plain": 6200, "san": 5800 }