Skip to content

fix(String): type forwards as the native string methods - #10229

Closed
serhii-indyrct wants to merge 1 commit into
Effect-TS:mainfrom
serhii-indyrct:fix/string-native-forwards
Closed

serhii-indyrct wants to merge 1 commit into
Effect-TS:mainfrom
serhii-indyrct:fix/string-native-forwards

Conversation

@serhii-indyrct

@serhii-indyrct serhii-indyrct commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Several String functions forward to a native string method but type their arguments or result narrower than the method does:

  • replace and replaceAll take only a replacement string, while the native methods also take a replacer function.
  • matchAll types each match as RegExpMatchArray, where index and input are optional, while String.prototype.matchAll yields RegExpExecArray, where both are always set. The matchAll JSDoc example reads match.index and gets number | undefined.
  • localeCompare takes locales only as a mutable Array<string>, so a single locale like "sv" doesn't compile. toLocaleLowerCase and toLocaleUpperCase refuse a readonly list and an Intl.Locale.
pipe("a-b", String.replace(/(\w)-(\w)/, (_, a, b) => `${b}-${a}`)) // type error on main
pipe("ä", String.localeCompare("z", "sv")) // type error on main

Fix

Each function now takes and returns what the native method does: a replacement string or a replacer function, RegExpStringIterator<RegExpExecArray> from matchAll, and Intl.LocalesArgument for locales, the type DateTime already uses. Nothing changes at runtime, and every new signature is assignable to the old one, so existing code keeps compiling.

replace and replaceAll branch on the replacement's type only so each call resolves one of the native overloads; a single call with the union doesn't compile.

Tests

Runtime tests in String.test.ts for replacer functions, the index and input of each match, and the locale arguments. Type tests in String.tst.ts, six of which fail on main.

pnpm lint, pnpm check, pnpm test-types --target '>=5.9', pnpm jsdocs --check, pnpm doctest, pnpm build with pnpm check-dist-types, and deno check . pass. String.test.ts passes on Node, Deno and Bun.

@changeset-bot

changeset-bot Bot commented Oct 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 879a41e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 33 packages
Name Type
effect Patch
@effect/opentelemetry Patch
@effect/vitest Patch
@effect/ai-anthropic Patch
@effect/ai-cloudflare Patch
@effect/ai-openai-compat Patch
@effect/ai-openai Patch
@effect/ai-openrouter Patch
@effect/ai-typesafe Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node-shared Patch
@effect/platform-node Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/graphql-generator Patch
@effect/openapi-generator Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@effect-janitor effect-janitor Bot added the 4.0 label Oct 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
arbitrary-combinators.ts 39.41 KB 39.41 KB 0.00 KB (0.00%)
basic.ts 7.23 KB 7.23 KB 0.00 KB (0.00%)
batching.ts 11.08 KB 11.08 KB 0.00 KB (0.00%)
brand.ts 6.87 KB 6.87 KB 0.00 KB (0.00%)
cache.ts 11.54 KB 11.54 KB 0.00 KB (0.00%)
config.ts 22.84 KB 22.84 KB 0.00 KB (0.00%)
differ.ts 22.00 KB 22.00 KB 0.00 KB (0.00%)
http-client.ts 26.95 KB 26.95 KB 0.00 KB (0.00%)
http-router.ts 37.02 KB 37.02 KB 0.00 KB (0.00%)
logger.ts 12.01 KB 12.01 KB 0.00 KB (0.00%)
metric.ts 9.47 KB 9.47 KB 0.00 KB (0.00%)
optic.ts 7.10 KB 7.10 KB 0.00 KB (0.00%)
pubsub.ts 16.03 KB 16.03 KB 0.00 KB (0.00%)
queue.ts 12.82 KB 12.82 KB 0.00 KB (0.00%)
schedule.ts 11.80 KB 11.80 KB 0.00 KB (0.00%)
schema-bigdecimal.ts 14.14 KB 14.14 KB 0.00 KB (0.00%)
schema-binary.ts 41.26 KB 41.26 KB 0.00 KB (0.00%)
schema-class.ts 21.73 KB 21.73 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 29.60 KB 29.60 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 27.99 KB 27.99 KB 0.00 KB (0.00%)
schema-string-transformation.ts 15.35 KB 15.35 KB 0.00 KB (0.00%)
schema-string.ts 12.34 KB 12.34 KB 0.00 KB (0.00%)
schema-template-literal.ts 16.27 KB 16.27 KB 0.00 KB (0.00%)
schema-toArbitrary.ts 38.95 KB 38.95 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 26.15 KB 26.15 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 20.90 KB 20.90 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 21.05 KB 21.05 KB 0.00 KB (0.00%)
schema-toFormatter.ts 21.14 KB 21.14 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 26.17 KB 26.17 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 21.23 KB 21.23 KB 0.00 KB (0.00%)
schema.ts 20.82 KB 20.82 KB 0.00 KB (0.00%)
stm.ts 14.20 KB 14.20 KB 0.00 KB (0.00%)
stream.ts 10.38 KB 10.38 KB 0.00 KB (0.00%)

@serhii-indyrct serhii-indyrct changed the title Type String forwards as the native string methods fix(String): type forwards as the native string methods Oct 31, 2026
@effect-bot

Copy link
Copy Markdown
Contributor

Thanks for the careful work here, @serhii-indyrct. We're closing this without merging because we only accept backward-compatible changes in minor releases, and this one can break existing code at the type level:

  • Return type of matchAll: with RegExpStringIterator<RegExpExecArray>, strictBuiltInIteratorReturn types the completion value as undefined. So S.matchAll(re)(s).next().value[0] stops compiling under strict.
  • Wider inputs to replace/replaceAll and the locale functions: Parameters<typeof S.replace>[1] is no longer string. Implementations and adapters written against the narrower signatures stop being assignable to typeof S.replace / typeof S.localeCompare. Higher-order overload selection can also change.

We also looked at keeping the original signature as a trailing overload. That still breaks the assignability case, so no subset of the change is fully non-breaking. We may revisit this in a future major release.

@effect-bot effect-bot closed this Nov 1, 2026

This branch is waiting to be deployed

1 waiting deployment
fork — 879a41ef Waiting Oct 31, 2026 by serhii-indyrct via approval-gate #24466
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants