Narrow a union to its only member of the typeof kind - #329
Merged
Merged
Conversation
`typeof u === "function"` (also "class", "interface", "object", "array") names a kind, not a type, so it narrowed `u` to Opaque (Opaque[] for "array"). For a union that threw the member type away: - `let f: (() => number) | string; if (typeof f === "function") f()` failed with "not a function to call"; - `let c: Box | number; if (typeof c === "class") c.value` failed with "Can't resolve property 'value' of type Opaque". A union already lists its members of that kind: the members whose runtime tag, typeOfAsString, is the tested name. When there is exactly one, checkSafeCastTypeOf now narrows the union to that member. With several members of the kind the value stays Opaque. The cast helper castFromUnion generates (`if (typeof t == 'function') return t;`) relies on it: narrowed to a smaller union, `return t` is a union cast again and instantiates the helper without end (01union_type and logicalAssignment5_3 overflowed the stack, typeGuardFunction threw at run time). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Problem
typeof u === "function"(also"class","interface","object","array") names a kind, not a type, so narrowing castutoOpaque(Opaque[]for"array"). For a union that threw the member type away:let f: (() => number) | string; if (typeof f === "function") f()failed with "not a function to call".let c: Box | number; if (typeof c === "class") c.valuefailed with "Can't resolve property 'value' of type Opaque".Fix
A union already lists its members of that kind: the members whose runtime tag,
TypeOfOpHelper::typeOfAsString, is the tested name. When there is exactly one,checkSafeCastTypeOfnow narrows the union to that member through the newgetUnionMembersOfTypeOf. Theelsebranch and!==get the remaining members, as for the othertypeofnames.With several members of the kind the value stays
Opaque, as before. The cast helper thatcastFromUniongenerates (if (typeof t == 'function') return t;) depends on it: narrowed to a smaller union,return tis a union cast again and instantiates the helper without end. A first version that narrowed to a union of the members failed01union_typeandlogicalAssignment5_3(stack overflow) andtypeGuardFunction(thrown at run time).Non-union values are #328's case and are not touched here.
Tests
New
00typeof_union_narrowing.ts(compile, JIT and corpus variants): calling the function member,!==leaving the string member, reading a class member's field, and length and element of an array member.Full Windows debug suite (
ctest -R "^test-"): 2745/2745 passed. After mergingmain(with #328), the 194 typeof, union, type-guard and logical-assignment tests pass again.Found while testing, not fixed here
let u: number[] | string = [1, 2]prints "error: source array and destination array have different types, src: 'si32' dst: '!ts.number'", yet the compile exits 0 and the stored array is garbage (its length reads as a pointer value). The test uses an explicitly typednumber[]source to stay clear of it.🤖 Generated with Claude Code