Skip to content

[Bug]: [http-server-csharp] Model with a Record<unknown>[] property generates C# that does not compile (missing using System.Text.Json.Nodes;) #11733

Description

Describe the bug

Package: @typespec/http-server-csharp 0.58.0-alpha.30

Description

A model property typed as an array of Record<unknown> is emitted as JsonObject[], but the
generated model file does not include using System.Text.Json.Nodes;, so the generated project
fails to compile with CS0246: The type or namespace name 'JsonObject' could not be found.

A property typed as Record<unknown> directly (non-array) works correctly — the using
directive is emitted.

Reproduction

Repro

Playground link

import "@typespec/http";
using Http;

@service(#{ title: "Repro" })
namespace Repro;

model DatasetRow is Record<unknown>;

model Page {
  items: DatasetRow[]; // array of Record<unknown> -> JsonObject[]
}

@route("/rows")
interface Rows {
  @get list(): Page;
}

Actual output

generated/models/Page.cs (note: no System.Text.Json.Nodes):

using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using TypeSpec.Helpers;
using TypeSpec.Helpers.JsonConverters;

namespace Repro
{
    public partial class Page
    {
        [JsonPropertyName("items")]
        public required JsonObject[] Items { get; set; }  // CS0246
    }
}

Expected output

The same file with using System.Text.Json.Nodes; included, as already happens when a property
is typed Record<unknown> without the array.

Root cause

modelNeedsJsonNodes in src/components/models/model-helpers.ts decides whether a model file
gets the System.Text.Json.Nodes using. It checks whether a property's type is a
Record<unknown> model, but never unwraps array types, so Record<unknown>[] (an Array model
whose indexer value is the Record) is missed — while the type-expression emission for the same
property does produce JsonObject[].

Suggested fix

Unwrap array indexers before the record check (loop so nested arrays are covered):

/** Returns true if any property of the model uses Record<T> (mapped to JsonObject). */
export function modelNeedsJsonNodes($: Typekit, model: Model): boolean {
  for (const prop of model.properties.values()) {
    // Unwrap array types: Record<unknown>[] emits JsonObject[] and needs JsonNodes too
    let type = prop.type;
    while (type.kind === "Model" && $.array.is(type) && type.indexer?.value) {
      type = type.indexer.value;
    }
    if (type.kind === "Model" && $.record.is(type)) {
      // Only need JsonNodes for Record<unknown> (maps to JsonObject)
      const valueType = type.indexer?.value;
      if (valueType?.kind === "Intrinsic" && valueType.name === "unknown") return true;
    }
  }
  return false;
}

We are running this change as a patch-package patch against the compiled dist output and can
confirm it fixes the compilation error without affecting the existing non-array behavior.

Checklist

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions