Skip to content

Generic type no longer narrowed as expected without extends object #48468

Closed
@mjbvz

Description

@mjbvz

Bug Report

πŸ”Ž Search Terms

πŸ•— Version & Regression Information

  • Regression in: 4.7.0-dev.20220325
  • Works in 4.7.0-dev.20220323

⏯ Playground Link

Playground currently doesn't show the error:

Playground link

πŸ’» Code

function deepEquals<T>(a: T, b: T): boolean {
    if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) {
        return false;
    }
    if (Array.isArray(a) || Array.isArray(b)) {
        return false;
    }
    if (Object.keys(a).length !== Object.keys(b).length) { // Error here
        return false;
    }
    return true;
}

πŸ™ Actual behavior

The calls to Object.keys generate errors:

No overload matches this call.
  Overload 1 of 2, '(o: {}): string[]', gave the following error.
    Argument of type 'T' is not assignable to parameter of type '{}'.
  Overload 2 of 2, '(o: object): string[]', gave the following error.
    Argument of type 'T' is not assignable to parameter of type 'object'.

πŸ™‚ Expected behavior

No errors

Activity

mjbvz

mjbvz commented on Mar 29, 2022

@mjbvz
ContributorAuthor

This code previously worked for us in VS Code but is now causing around 11 errors in our codebase. Not sure if simply adding a extends object constraint is the correct fix in all cases

changed the title [-]Types no longer narrowed as expected [/-] [+]Generic type no longer narrowed as expected without `extends object`[/+] on Mar 29, 2022
whzx5byb

whzx5byb commented on Mar 29, 2022

@whzx5byb

It seems to be caused by #48366, which is tagged as "Breaking Change".

Now you can't assign a unconstrained type parameter T to T extends {} or T extends object. See also #37388

DanielRosenwasser

DanielRosenwasser commented on Mar 29, 2022

@DanielRosenwasser
Member

CC @weswigham

Looks like the following will work

function deepEquals<T extends {} | null | undefined>(a: T, b: T): boolean {
    if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) {
        return false;
    }
    if (Array.isArray(a) || Array.isArray(b)) {
        return false;
    }
    if (Object.keys(a).length !== Object.keys(b).length) {
        return false;
    }
    return true;
} 

and so will

function deepEquals<T extends object | null | undefined>(a: T, b: T): boolean {
    if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) {
        return false;
    }
    if (Array.isArray(a) || Array.isArray(b)) {
        return false;
    }
    if (Object.keys(a).length !== Object.keys(b).length) {
        return false;
    }
    return true;
} 

but not

function deepEquals<T extends unknown>(a: T, b: T): boolean {
    if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) {
        return false;
    }
    if (Array.isArray(a) || Array.isArray(b)) {
        return false;
    }
    if (Object.keys(a).length !== Object.keys(b).length) {
        return false;
    }
    return true;
} 

Note that the first two only likely work due to @ahejlsberg's change in #43183. I don't know if we're willing to do something special for unknown or all non-unions.

added a commit that references this issue on Mar 29, 2022
mjbvz

mjbvz commented on Mar 29, 2022

@mjbvz
ContributorAuthor

Also seeing this in the @playwright/test package. See this issue for repo steps and details about the error

DanielRosenwasser

DanielRosenwasser commented on Apr 5, 2022

@DanielRosenwasser
Member

Playing around with a fix here FWIW

#48576

arnold-O

arnold-O commented on Feb 9, 2023

@arnold-O

The Generic type without constraint is vague, hence with the extends keyword you clearly state what you want that Generic type to be, which is the reason for Typescript.

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

Metadata

Metadata

Labels

Breaking ChangeWould introduce errors in existing codeFix AvailableA PR has been opened for this issue

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Participants

    @DanielRosenwasser@whzx5byb@RyanCavanaugh@mjbvz@typescript-bot

    Issue actions

      Generic type no longer narrowed as expected without `extends object` Β· Issue #48468 Β· microsoft/TypeScript