Skip to content

Commit c977d78

Browse files
authored
fix(js/ts): pass TypeInfo to getRecordFields to handle None fields in anonymous records (#4704)
1 parent 0de0d21 commit c977d78

3 files changed

Lines changed: 22 additions & 3 deletions

File tree

src/Fable.Transforms/Replacements.fs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4282,7 +4282,13 @@ let fsharpValue com methName (r: SourceLocation option) t (i: CallInfo) (args: E
42824282
// concept the runtime helpers don't accept.
42834283
let args =
42844284
match methName with
4285-
| "GetRecordFields" -> List.truncate 1 args
4285+
| "GetRecordFields" ->
4286+
let recordArg = List.head args
4287+
// For anonymous records, None fields are omitted from the JS object so
4288+
// Object.keys cannot enumerate them. Pass the TypeInfo so the runtime can
4289+
// use field names from the type instead of from the object's own keys.
4290+
let (MaybeCasted innerArg) = recordArg
4291+
[ recordArg; makeTypeInfo r innerArg.Type ]
42864292
| "GetUnionFields"
42874293
| "MakeUnion"
42884294
| "MakeRecord" -> List.truncate 2 args

src/fable-library-ts/Reflection.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -488,8 +488,14 @@ export function getUnionCaseFields(uci: CaseInfo): FieldInfo[] {
488488

489489
// This is used as replacement of `FSharpValue.GetRecordFields`
490490
// For `FSharpTypes.GetRecordFields` see `getRecordElements`
491-
// Object.keys returns keys in the order they were added to the object
492-
export function getRecordFields(v: any): MutableArray<any> {
491+
// TypeInfo is used when available to enumerate fields by name: anonymous record None fields
492+
// are omitted from the JS object, so Object.keys alone would miss them.
493+
// Boxed anonymous record would are still not covered but this is the best we can do for now
494+
// without adding a __fields__ to every anonymous record being created
495+
export function getRecordFields(v: any, t: TypeInfo): MutableArray<any> {
496+
if (t.fields != null) {
497+
return t.fields().map(([key, _]) => v[key]);
498+
}
493499
return Object.keys(v).map((k) => v[k]);
494500
}
495501

tests/Js/Main/ReflectionTests.fs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,13 @@ let reflectionTests = [
381381
let all = isRecord && matchRecordFields && matchIndividualRecordFields && canMakeSameRecord
382382
all |> equal true
383383

384+
testCase "FSharpValue.GetRecordFields with anonymous record returns all fields including None" <| fun () ->
385+
let fields = FSharpValue.GetRecordFields {| a = 3; b = (None: int option); c = Some 89 |}
386+
fields.Length |> equal 3
387+
fields.[0] |> equal (box 3)
388+
fields.[1] |> equal (box None)
389+
fields.[2] |> equal (box (Some 89))
390+
384391
testCase "Reflection functions accept allowAccessToPrivateRepresentation" <| fun () ->
385392
let recordType = typeof<TestRecord>
386393
let record = { String = "a"; Int = 1 }

0 commit comments

Comments
 (0)