Skip to content

Type Guard for function property leads to overloading #14820

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
arusakov opened this issue Mar 23, 2017 · 3 comments
Closed

Type Guard for function property leads to overloading #14820

arusakov opened this issue Mar 23, 2017 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@arusakov
Copy link
Contributor

arusakov commented Mar 23, 2017

TypeScript Version: 2.2.1

I'm confused with type guard and function property.
Type Guard can redefine property from number | undefined to number (useful for strictNullChecks mode)
But for functions such type guard has added overloading.

Code

// --strictNullChecks

class X {
  checkValSimple(): this is X & { valSimple: number } {
    return true
  }
  valSimple: number | undefined;

  checkValGetter(): this is X & { valGetter: number } {
    return true
  }
  get valGetter(): number | undefined {
    return 1
  }

  hasValFunc(): this is X & { valFunc(): number } {
    return true
  }
  valFunc(): number | undefined {
    return 1
  }
}

const x = new X();
if (x.checkValSimple()) {
  x.valSimple.toFixed() // ok
}

if (x.checkValGetter()) {
  x.valGetter.toFixed() // ok
}

if (x.hasValFunc()) {
  x.valFunc().toFixed() // Error: Object is possibly 'undefined'.
}

Expected behavior:
No error in the last case

Actual behavior:
In the last case valFunc() has two signatures:

  • (): number | undefined
  • (): number
@arusakov arusakov changed the title Type Guard with function leads to overloading Type Guard for function property leads to overloading Mar 23, 2017
@aluanhaddad
Copy link
Contributor

aluanhaddad commented Mar 27, 2017

That might seem surprising, but it is correct. Your type guard refines the type of this such that it has a --strictNullChecks distinct overload. Based on your member names, I think you may have a logic error.
Specifically, the name hasValFunc suggests the following definition rather than what you currently have.

class X {
  hasValFunc(): this is X & { valFunc(): number } {
    return !!this.valFunc;
  }
  valFunc?: () => number = () => {
    return 1;
  };
}

this changes the semantics of your class, making the function a per instance property instead of a method on X.prototype, but I think that this is also correct.

@arusakov
Copy link
Contributor Author

arusakov commented Mar 27, 2017

@aluanhaddad
Previous example is synthetic, I can provide the better one:

// --strictNullChecks

type XData = {
  flags: {
    hasProp1: boolean,
  },
  prop1?: number,
}

class X {
  protected data: XData;

  hasProp1(): this is this & { getProp1(): number } {
    return this.data.flags.hasProp1
  }

  hasProp1Alternative(): this is this & { data: { prop1: number } } {
    return this.data.flags.hasProp1
  }

  getProp1() {
    return this.data.prop1
  }
}

const x = new X()
if (x.hasProp1()) {
  x.getProp1().toFixed() // error
}
if (x.hasProp1Alternative()) {
  x.getProp1().toFixed() // error
  x.data.prop1.toFixed() // ok, but this breaks down encapsulation
}

Here are a few main reasons:

  • I have complex data struct and I want to encapsulate this inside class
  • I want to use manual getters (for example for es3 compatibility)
  • I don't want to access possibly nonexistent (prop1?) property (this leads to deoptimization in V8)

Maybe there is alternative way, but in my example TypeScript doesn't provide me what I want (via both hasProp1() and hasProp1Alternative())

Simple summary:
I want to change return type of an object method via using guard function. But it's not possible.

@mhegazy
Copy link
Contributor

mhegazy commented Mar 28, 2017

This is not the intended use of type guards as they stand today. Moreover, the intersection behavior you are seeing is consistent with the semantics of the type operator; a intersecting two signatures result in overloads. I believe you are looking for something like #11117.

@mhegazy mhegazy added the Duplicate An existing issue was already created label Mar 28, 2017
@mhegazy mhegazy closed this as completed Apr 21, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 21, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants