Skip to content

never rest type not assignable to complex rest type #33495

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

Closed
jack-williams opened this issue Sep 18, 2019 · 8 comments · Fixed by #35438
Closed

never rest type not assignable to complex rest type #33495

jack-williams opened this issue Sep 18, 2019 · 8 comments · Fixed by #35438
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@jack-williams
Copy link
Collaborator

TypeScript Version: 3.5.1

Search Terms:

Code

function foo<T extends any[]>() {
    const fn1: (x: string, ...rest: T) => unknown = (x, ..._) => x;
    const fn2: (...args: never) => unknown = fn1 // error
}

Expected behavior:

Assignment of fn1 to fn2 should pass.

Actual behavior:

Assignment of fn1 to fn2 should fails.

Playground Link: link

Related Issues:
#33457

@jack-williams
Copy link
Collaborator Author

The goal here would be to make all signatures structurally assignable to (...args: never) => unknown.

@RyanCavanaugh
Copy link
Member

@ahejlsberg this seems reasonable to me. Thoughts?

@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Sep 18, 2019
@fatcerberus
Copy link

fatcerberus commented Sep 18, 2019

I did some experiments in the playground. This seems to break only when the rest parameter is generic. Use a concrete type and all is well:

Interestingly, [ string, ...T ] (where T is generic but extends any[]) isn't even legal and produces the error A rest element type must be an array type. This seems like a pretty big clue: the assignment of fn1 to fn2 may only be failing because it's unable to construct a tuple type for the parameter list of (x: string, ...rest: T) => unknown.

@jack-williams
Copy link
Collaborator Author

This is where it fails in checking:

const sourceCount = getParameterCount(source);
const sourceRestType = getNonArrayRestType(source);
const targetRestType = getNonArrayRestType(target);
if (sourceRestType && targetRestType && sourceCount !== targetCount) {
    // We're not able to relate misaligned complex rest parameters
    return Ternary.False;
}

@fatcerberus
Copy link

@jack-williams That doesn't seem to explain why it succeeds for the non-generic case. What is the exact definition of "complex" here?

@jack-williams
Copy link
Collaborator Author

jack-williams commented Sep 18, 2019

What is the exact definition of "complex" here?

A rest type where you don't know the bounds, or whether it is unbounded---I would guess. I think never needs to be treated like any in that it is effectively known to be unbounded.

@jack-williams
Copy link
Collaborator Author

FWIW. I think the fix is basically:

function getNonArrayRestType(signature: Signature) {
    const restType = getEffectiveRestType(signature);
    return restType && !isArrayType(restType) && !isTypeAny(restType) && !isTypeNever(restType) ? restType : undefined;
}

+ isTypeNever.

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Dec 1, 2019

Repro taken from TS' source code,

/**
* Safer version of `Function` which should not be called.
* Every function should be assignable to this, but this should not be assignable to every function.
*/
export type AnyFunction = (...args: never[]) => void;

/**
 * Safer version of `Function` which should not be called.
 * Every function should be assignable to this, but this should not be assignable to every function.
 */
export type AnyFunction = (...args: never[]) => void;

function foo<T extends any[]>() {
    const fn1: (...rest: T) => void = (..._) => {};
    const fn2: AnyFunction = fn1 // error
}

Playground

Every function should be assignable to this

But not every function is assignable to it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants