Skip to content

Assignment to an any-parameterised generic rejected by tsgo, accepted by tsc 6.0.3 #63881

Closed as not planned

Description

Steps to reproduce

In an empty directory:

npm init -y
npm install typescript@6.0.3 yup@1.4.0

repro.ts:

import { AnyObjectSchema, ObjectSchema, number, object, string } from "yup";

interface FormData {
  name: string;
  amount: number;
}

const schema: ObjectSchema<FormData> = object({
  name: string().required(),
  amount: number().required(),
});

const widened: AnyObjectSchema = schema;

export { widened };
npx tsc  --noEmit --strict --target es2020 repro.ts
npx tsgo --noEmit --strict --target es2020 repro.ts

AnyObjectSchema is yup's own alias for ObjectSchema<any, any, any, any>.

Reproduces with and without --skipLibCheck, and also when yup's declarations are copied into a local .d.ts.

Behavior with typescript@6.0

Compiles clean, exit code 0.

Also clean with --stableTypeOrdering, so this is not explained by deterministic type ordering:

tsc --noEmit --strict --target es2020 repro.ts                        # exit 0
tsc --noEmit --strict --target es2020 --stableTypeOrdering repro.ts   # exit 0

Behavior with tsgo

repro.ts(13,7): error TS2322: Type 'ObjectSchema<FormData, AnyObject, any, "">' is not assignable to type 'AnyObjectSchema'.
  The types returned by 'optional().concat' are incompatible between these types.
    Type '{ <IIn extends Maybe<AnyObject>, IC, ID, IF extends Flags>(schema: ObjectSchema<IIn, IC, ID, IF>): ObjectSchema<ConcatObjectTypes<FormData | undefined, IIn>, AnyObject & IC, ..., "" | IF>; (schema: ObjectSchema<...>): ObjectSchema<...>; }' is not assignable to type '{ <IIn extends Maybe<AnyObject>, IC, ID, IF extends Flags>(schema: ObjectSchema<IIn, IC, ID, IF>): ObjectSchema<ConcatObjectTypes<any, IIn>, any, Extract<IF, "d"> extends never ? ID | ... : ID, any>; (schema: ObjectSchema<...>): ObjectSchema<...>; }'.
      Types of parameters 'schema' and 'schema' are incompatible.
        Type 'ObjectSchema<any, any, any, any>' is not assignable to type 'ObjectSchema<FormData | undefined, AnyObject, any, "">'.
          The types returned by 'default(...).fields' are incompatible between these types.
            Type 'Shape<any, any>' is missing the following properties from type 'Shape<FormData, AnyObject>': name, amount

Exit code 1. Confirmed failing on 7.0.2, 7.1.0-dev.20260807.1 and 7.1.0-dev.20260810.1.

Analysis

The last line looks like the crux. The relevant yup declarations are:

type Shape<T extends Maybe<AnyObject>, C = any> = {
  [field in keyof T]-?: ISchema<T[field], C> | Reference;
};

declare class ObjectSchema<TIn extends Maybe<AnyObject>, TContext = AnyObject, ...> {
  fields: Shape<NonNullable<TIn>, TContext>;
  concat<IIn extends Maybe<AnyObject>, IC, ID, IF extends Flags>(
    schema: ObjectSchema<IIn, IC, ID, IF>
  ): ObjectSchema<...>;
}

Shape<any, any> is a mapped type over keyof any, so it should be compatible with any instantiation — but it is reported as missing properties. any appears to stop absorbing the structural difference once the check recurses into the generic concat parameter position.

Narrowing

It only reproduces when the schema type is explicitly annotated. Holding the construction identical and changing only the annotation flips it:

// fails on tsgo
const annotated: ObjectSchema<FormData> = object({
  name: string().required(),
  amount: number().required(),
});
const a: AnyObjectSchema = annotated;   // TS2322 on tsgo, fine on tsc 6

// passes on tsgo -- identical construction, type left inferred
const checked = object({
  name: string().required(),
  amount: number().required(),
}) satisfies ObjectSchema<FormData>;
const b: AnyObjectSchema = checked;     // fine on both

satisfies performs the same assignability check, so the difference is purely whether the declared instantiation is what flows into the comparison. Same result via a factory returning object().shape({ ... }), annotated ): ObjectSchema<FormData> => versus return schema satisfies ObjectSchema<FormData> — so it is the annotation, not the construction form.

We hit this because two otherwise identical call sites in our codebase behaved differently: the one whose schema factory declared ): ObjectSchema<T> => failed, the one that let the return type infer did not.

I could not reduce this to a dependency-free case. Six hand-written models of the shape (mapped type with -?, Shape<NonNullable<TIn>>, values depending on T[K], interface/declare class merging, a this-typed concat overload, four type parameters with a conditional return type) all compiled clean on both compilers, so the trigger appears subtler than the error text suggests. A declaration-level delta-debugger over yup's own index.d.ts only got 893 → 776 lines without --skipLibCheck.

Switching those factories to satisfies is what we shipped, so we are not blocked — but it looks like a genuine divergence worth fixing.

Activity

  1. mrazauskas commented on Aug 10, 2026

    @mrazauskas
    Contributor

    Could you double-check, please? Something might be mixed up here, because the reproduction steps in the OP are identical to microsoft/typescript-go#4862

  2. richardquaite commented on Aug 10, 2026

    @richardquaite
    Author

    Sorry about that, Please see updated issue

  3. ahejlsberg commented on Aug 14, 2026

    @ahejlsberg
    Member

    This is working as intended and is an indirect effect of microsoft/typescript-go#3445. With microsoft/typescript-go#3445 the checker more deeply analyses relationships between different instantiations of ObjectSchema. This in turn reveals that Shape<any, any> is not assignable to Shape<FormData, AnyObject> which ultimately is because the type { [x: keyof any]: any } (which reduces to { [x: string]: any }) isn't assignable to a type that has concrete properties. In TS6 (i.e. before microsoft/typescript-go#3445), analysis of ObjectSchema instantiation relationships would terminate before the checker got deep enough to reveal the incompatibility.

  4. added
    Working as IntendedThe behavior described is the intended behavior; this is not a bug
    and removed
    Needs InvestigationThis issue needs a team member to investigate its status.
    on Aug 14, 2026
  5. typescript-automation commented on Aug 23, 2026

    @typescript-automation
    Contributor

    This issue has been marked as "Working as Intended" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Working as IntendedThe behavior described is the intended behavior; this is not a bug

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions