fix(utilities): isObject narrows to Record<string, any> for correct interface narrowing - #746
Merged
Merged
Conversation
The narrowing table claimed `Record<string, unknown>` for isObject and `unknown[]` for isArray. Both are stale: isArray became element-type preserving in #745, and isObject's predicate widens in this branch. Footnotes carry the detail that does not fit a table cell — why the index type is `any`, and what isArray preserves.
isObject now narrows to `Record<string, any>`, so reading $value and the child entries through it yields `any`. That makes the isObject/'$value' in guards below compile-optional and lets the subsequent casts assert against an unchecked type. Annotating both reads as `unknown` restores the obligation to narrow before use. Type-only — no runtime or public API change.
… exception §2.2 claimed a single sanctioned any (slot returns). isObject's predicate is a second, measured exception — Record<string, unknown> is incorrect narrowing for interfaces and negative branches (#723). §8.2 now states the unknown-input caveat so "narrow with isObject" is not read as restoring checked property access.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #723
What
isObjectpreviously narrowed toRecord<string, unknown>. That is incorrect for two TypeScript reasons:unknown.Record<string, any>from a union, so object members surviveelse.The predicate is now
Record<string, any>. Runtime is unchanged.In-package instance of the fix:
createContexttakesCreateContextOptions(an interface); after this changekeyOrOptions.suffixis properlystring | undefinedinstead of silentlyunknown.Why
Measured against vuetifyjs/vuetify#23039 (core adopting v0 guards): only this signature clears the four core typecheck failures (VTabs, mask, transition, tooltip). Sibling #745 found a zero-
anypath forisArray; that does not transfer here — everyunknown-preserving candidate failed or matched the status quo.Also in this PR
REFERENCE.mdupdated forisObjectand the already-staleisArrayrow from fix(utilities): preserve element types, tuples and readonly in isArray narrowing #745.createTokens.flatten— pin$value/ child reads tounknownso recursive guards and casts stay real under the widened index type (typecheck stays green underanywithout this).anyexception documented; unknown-input caveat onisObject.isPlainObject(private, drivesmergeDeep) stays onRecord<string, unknown>deliberately — not a public predicate; keeps index reads checked. No change tomergeDeep's public signature.Tests
Type-level narrowing assertions in
helpers.test.ts(same harness as #745):string | boolean | Record<string, any>drops the Record in the negative branchNot breaking — runtime identical; the widened predicate is assignable from the old one for any program that already typechecked.