Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
mjbvz opened this issue Mar 29, 2022 · 6 comments Β· Fixed by #49119
Closed

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

mjbvz opened this issue Mar 29, 2022 · 6 comments Β· Fixed by #49119
Assignees
Labels
Breaking Change Would introduce errors in existing code Fix Available A PR has been opened for this issue

Comments

@mjbvz
Copy link
Contributor

mjbvz commented Mar 29, 2022

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

@mjbvz
Copy link
Contributor Author

mjbvz commented Mar 29, 2022

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

@mjbvz mjbvz changed the title Types no longer narrowed as expected Generic type no longer narrowed as expected without extends object Mar 29, 2022
@whzx5byb
Copy link

whzx5byb commented Mar 29, 2022

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
Copy link
Member

DanielRosenwasser commented Mar 29, 2022

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.

@mjbvz
Copy link
Contributor Author

mjbvz commented Mar 29, 2022

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

@DanielRosenwasser
Copy link
Member

Playing around with a fix here FWIW

#48576

@arnold-O
Copy link

arnold-O commented Feb 9, 2023

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
Labels
Breaking Change Would introduce errors in existing code Fix Available A PR has been opened for this issue
Projects
None yet
6 participants