Typescript: never rest type not assignable to complex rest type

Created on 18 Sep 2019  路  8Comments  路  Source: microsoft/TypeScript


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

In Discussion Suggestion

Most helpful comment

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

All 8 comments

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

@ahejlsberg this seems reasonable to me. Thoughts?

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.

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;
}

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

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.

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.

Repro taken from TS' source code,
https://github.com/microsoft/TypeScript/blob/c447ebc59c49a1b071c099ce6055e94517973eab/src/compiler/core.ts#L1460-L1464

/**
 * 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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dlaberge picture dlaberge  路  3Comments

CyrusNajmabadi picture CyrusNajmabadi  路  3Comments

jbondc picture jbondc  路  3Comments

weswigham picture weswigham  路  3Comments

manekinekko picture manekinekko  路  3Comments