Skip to content

Applying a constrained generic with type parameters as the argument type doesn't properly catch constrained fields #52309

Closed

Description

Bug Report

🔎 Search Terms

generic constraints, generic types, multiple generics, generic type variable, type parameters

🕗 Version & Regression Information

This is the behavior in every version I tried, and I reviewed the FAQ for entries about generics.

⏯ Playground Link

Playground link with relevant code

💻 Code

type PropertiesType = {[key:string]: any};
type RequiredPropertiesType<Props extends PropertiesType> = (Exclude<keyof Props, symbol>)[];

interface JSONSchemaObjectType<
  Props extends PropertiesType,
  RequiredProps extends RequiredPropertiesType<Props>
> {
  properties: Props;
  required?: RequiredProps;
  additionalProperties?: boolean;
}

const DefineObject = <
  Props extends PropertiesType,
  RequiredProps extends RequiredPropertiesType<Props>,
  Def extends JSONSchemaObjectType<Props, RequiredProps>
>(definition: Def) => {
  // noop
}

DefineObject({
  properties: {
    test: "hi"
  },
  required: ["test", "broken like me"] // <-- no static type error
})

🙁 Actual behavior

The required array accepts values that are not keys used in it's sibling properties

🙂 Expected behavior

The required array is constrained to the keys used in it's sibling properties

Activity

  1. whzx5byb commented on Jan 20, 2023

    @whzx5byb

    You should explicitly provide the constraint as a context to help infer the correct type of Props:

    const DefineObject = <
      Props extends PropertiesType,
      RequiredProps extends RequiredPropertiesType<Props>,
      Def extends JSONSchemaObjectType<Props, RequiredProps>
    - >(definition: Def) => {
    + >(definition: Def & JSONSchemaObjectType<Props, RequiredProps>) => {
    }
    
  2. shapirone commented on Jan 20, 2023

    @shapirone
    Author

    whzx5byb this constrains the argument but causes any conditional typing that relies on definition being a generic to fail

  3. RyanCavanaugh commented on Jan 26, 2023

    @RyanCavanaugh
    Member

    this constrains the argument but causes any conditional typing that relies on definition being a generic to fail

    Can you go into more detail? The above solution is effectively correct; there's not a way to exclude candidates from inference like you would need in order to make this to work.

  4. RyanCavanaugh commented on Feb 2, 2023

    @RyanCavanaugh
    Member

    Treating as duplicate of #14829

  5. shapirone commented on Feb 2, 2023

    @shapirone
    Author

    Sorry for the silence here, I've been looking into the resulting conditional typing failure and haven't been able to reproduce what we're seeing in a simplified TS Playground. Applying the recommended solution appropriately applied the constraint on the required field, but caused static type test failures that I assumed were related to inference from our conditional typing.

    I've since realized that the test failures we're seeing are resolved if we assign the result of DefineObject to a variable and reference that variable in the surrounding function call, rather than using DefineObject directly as the object value. Code sample below to illustrate

    // This causes test failures in our inferred runtime typing
    DefineFunction({
      input_parameters: {
        properties: {
          myObject: DefineObject({
            type: Schema.types.object,
            properties: { req: { type: "string" }, opt: { type: "string" } },
            required: ["req"],
          });
        },
        required: ["myObject"],
      },
    });
    
    
    // This does not
    const MyObject = DefineObject({
      type: Schema.types.object,
      properties: { req: { type: "string" }, opt: { type: "string" } },
      required: ["req"],
    });
      
    DefineFunction({
      input_parameters: {
        properties: {
          myObject: MyObject
        },
        required: ["anObject"],
      },
    });

    I'm still working on providing a Playground example, but please let me know if this behavior is familiar to you, if I should close this issue and open a new issue when I have more details, or if a pointer to where this creates problems in our actual code base would be helpful 🙇

  6. typescript-bot commented on Feb 4, 2023

    @typescript-bot
    Contributor

    This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

  7. locked as resolved and limited conversation to collaborators on Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions