Skip to content

Root file order causes tsgo to miss recursive conditional type assignability error #4647

Description

@tomquist

Steps to reproduce

Create these two dependency-free files.

warmup.ts

type SomeJSONSchema = UncheckedJSONSchemaType<Known, true>

type JSONType<T extends string, Partial extends boolean> =
  Partial extends true ? T | undefined : T

type UncheckedJSONSchemaType<T, Partial extends boolean> = (
  | { anyOf: readonly UncheckedJSONSchemaType<T, Partial>[] }
  | (T extends string
      ? { type: JSONType<"string", Partial> }
      : T extends readonly any[]
        ? {
            type: JSONType<"array", Partial>
            items: UncheckedJSONSchemaType<T[0], false>
          }
        : T extends Record<string, any>
          ? {
              type: JSONType<"object", Partial>
              properties?: Partial extends true
                ? Partial<UncheckedPropertiesSchema<T>>
                : UncheckedPropertiesSchema<T>
              required: readonly (keyof T)[]
            }
          : never)
) & { [keyword: string]: any }

type JSONSchemaType<T> = UncheckedJSONSchemaType<T, false>

type Known = { [key: string]: Known } | Known[] | string

type UncheckedPropertiesSchema<T> = {
  [K in keyof T]-?: UncheckedJSONSchemaType<T[K], false> & Nullable<T[K]>
}

type Nullable<T> = {
  enum?: readonly T[]
}

interface CompletionParameters {
  responseFormat?:
    | { type: "json_object" }
    | {
        type: "json_schema"
        json_schema: { schema?: SomeJSONSchema }
      }
}

interface Item {
  value: string
}

const itemSchema: JSONSchemaType<Item> = {
  type: "object",
  properties: { value: { type: "string" } },
  required: ["value"],
}

interface WarmResult {
  items: Item[]
}

const warmResultSchema: JSONSchemaType<WarmResult> = {
  type: "object",
  properties: {
    items: { type: "array", items: itemSchema },
  },
  required: ["items"],
}

target.ts

interface Result {
  items: Item[]
}

const resultSchema: JSONSchemaType<Result> = {
  type: "object",
  properties: {
    items: { type: "array", items: itemSchema },
  },
  required: ["items"],
}

class TargetPrompt {
  completionParameters: CompletionParameters = {
    responseFormat: {
      type: "json_schema",
      json_schema: { schema: resultSchema },
    },
  }
}

Versions tested:

  • TypeScript 6.0.3
  • tsgo 7.0.2, git commit 2bd066d87f5bafd315be9f40889d0a60b9e58e0b

Run the files in this order:

  • tsc --ignoreConfig --strict --noEmit --pretty false warmup.ts target.ts
  • tsgo --ignoreConfig --strict --noEmit --pretty false warmup.ts target.ts

Reversing the root-file order makes both compilers accept the program:

  • tsc --ignoreConfig --strict --noEmit --pretty false target.ts warmup.ts
  • tsgo --ignoreConfig --strict --noEmit --pretty false target.ts warmup.ts

Behavior with typescript@6.0

The warmup-first command exits with code 2:

target.ts(17,22): error TS2322: Type 'JSONSchemaType<Result>' is not assignable to type 'SomeJSONSchema | undefined'.
  Type '{ anyOf: readonly UncheckedJSONSchemaType<Result, false>[] } & { [keyword: string]: any }' is not assignable to type 'SomeJSONSchema | undefined'.

Behavior with tsgo

The same warmup-first command exits successfully with no diagnostics.

Expected behavior

tsgo should report the same assignability error as TypeScript 6 for the same root-file order.

warmResultSchema is unrelated to and unused by TargetPrompt. Its presence and root-file position should not change whether resultSchema is assignable to SomeJSONSchema.

The types are minimized from AJV’s recursive JSONSchemaType<T> and SomeJSONSchema types. AJV documents that these widened types are incompatible in ajv-validator/ajv#1988, so tsgo’s successful result appears to be a false negative. This may relate to recursive relation caching or bailout order.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type OrderingAn issue related to ordering of types

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions