Skip to content

Type narrowing on Function.length for either callable type #18422

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
mjbvz opened this issue Sep 12, 2017 · 4 comments
Closed

Type narrowing on Function.length for either callable type #18422

mjbvz opened this issue Sep 12, 2017 · 4 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@mjbvz
Copy link
Contributor

mjbvz commented Sep 12, 2017

TypeScript Version: 2.5.2

Code

interface IFoo {
  a: ((x: number) => string) | ((x: number, y: boolean) => string)
}

const z: IFoo = { 
  a(x: number) { return '' }
}

if (z.a.length === 1) {
  z.a(1)
}

Expected behavior:
Type of z.a is narrowed to (x: number) => string) from the if statement

Actual behavior:
Error:

'Cannot invoke an expression whose type lacks a call signature. Type '((x: number) => string) | ((x: number, y: boolean) => string)' has no compatible call signatures.'

I'm trying to express an interface that can implement a method in one of two ways. Let me know if there is a better way to express this

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Sep 13, 2017

I don't really know whether narrowing by length is the right thing since the underlying implementation might have nothing to do with how the signatures are described to TypeScript. In other words, you might have a parameter length of 0 if the function uses arguments under the cover.

I think it's worth considering whether it's the common enough case though, or whether it's fair game for single-signature types.

@DanielRosenwasser DanielRosenwasser added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Sep 13, 2017
@mjbvz
Copy link
Contributor Author

mjbvz commented Sep 13, 2017

Ok, that makes sense. Is there a better way to express the idea of an interface that implement a method one of two ways. I also tried:

interface X { x: number }

interface Y { y: number }

interface IFoo {
  a(x: X): string
  a(x: Y, y: boolean): string
}

const z: IFoo = { 
  a(x: X) { return '' }
} 

if (z.a.length <= 1) {
  z.a({ x: 1 })
}

But this fails to compile since types X and Y are not compatible

@mhegazy
Copy link
Contributor

mhegazy commented Nov 7, 2017

The type system really does not have a good way to model function.length, in almost all its uses. The only way that works today is to have the signature part of type that that has a discriminant property, e.g.:

type IFoo = {
    kind: "x",
    a(x: X): string
} | {
    kind: "y"
    a(x: Y, y: boolean): string
};

const z: IFoo = {
    kind: "x",
    a(x: X) { return '' }
};

if (z.kind === "x") {
  z.a({ x: 1 })
}

@mhegazy mhegazy added Design Limitation Constraints of the existing architecture prevent this from being fixed and removed In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Nov 7, 2017
@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

4 participants