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

function that returns union with void can be incorrectly narrowed #46837

Open
DetachHead opened this issue Nov 17, 2021 · 9 comments
Open

function that returns union with void can be incorrectly narrowed #46837

DetachHead opened this issue Nov 17, 2021 · 9 comments
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@DetachHead
Copy link
Contributor

Bug Report

πŸ”Ž Search Terms

void narrow

πŸ•— Version & Regression Information

4.6.0-20211117

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

const a: (value: number) => void = (i) => i
const b: (value: number) => void | string = a

const v = b(1)
if (v) {
    v.toUpperCase() //runtime error: v.toUpperCase is not a function 
}

πŸ™ Actual behavior

no error

πŸ™‚ Expected behavior

value doesn't get narrowed. perhaps void should not be falsy because you can't be sure its value will be falsy at runtime

@MartinJohns
Copy link
Contributor

Related: #42709

@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Nov 18, 2021
@RyanCavanaugh
Copy link
Member

I think we should do some patchwork and just disallow writing void | T in --strict. It's never what you want.

@KotlinIsland
Copy link

@RyanCavanaugh based

@mdbetancourt
Copy link

I think we should do some patchwork and just disallow writing void | T in --strict. It's never what you want.

i don't think so void !== undefined

function convert<T>(t: T): void | T { // ok
  if(typeof t === 'string') {
    return t
  }
}
function convert2<T>(t: T): undefined | T { // error here "A function whose declared type is neither 'void' nor 'any' must return a value"
  if(typeof t === 'string') {
    return t
  }
}

@RyanCavanaugh
Copy link
Member

@mdbetancourt noImplicitReturns means no implicit returns. If you don't like the error in the second case, you should turn that option off.

@mdbetancourt
Copy link

mdbetancourt commented Nov 18, 2021

what about this case extending his case

const a = (i: number[]) => i.forEach(console.log)

const b: (value?: number) => void | false = (v) => v ? a([v]) : false
//                             ^ undefined or anything else is not gonna work

@RyanCavanaugh
Copy link
Member

unknown is equally correct there. We could change forEach to return undefined and then it can be undefined | false.

@mdbetancourt
Copy link

mdbetancourt commented Nov 18, 2021

unknown is equally correct there.

@RyanCavanaugh unknown dont feel right, unknown means you don't know the value but you do also it absorbs the false type it's like an any

const a = (i: number[]) => i.forEach(console.log)

const b: (value?: number) => unknown | false = (v) => v ? a([v]) : false
const value = b() // unknown
if(typeof value === 'string') {
  value // type is "string" you think this is ok?
}

const b: (value?: number) => void | false = (v) => v ? a([v]) : false
const value = b() // void | false
if(typeof value === 'string') {
  value // type is "never" much better isn't?
}

We could change forEach to return undefined and then it can be undefined | false

but that could break some codes isn't? e.g:

function voidFn() {}

const log = [].forEach as () => undefined
const h: typeof log = voidFn
//    ^ Type 'void' is not assignable to type 'undefined'

a real case could be

function altLogger(text: string) {}

// if you change forEach signature you have to change every void signature as well, then
const log = console.log as (text: string) => undefined
const h: typeof log = altLogger
//    ^ Type 'void' is not assignable to type 'undefined'

@marcospgp
Copy link

marcospgp commented May 24, 2023

what about this case extending his case

const a = (i: number[]) => i.forEach(console.log)

const b: (value?: number) => void | false = (v) => v ? a([v]) : false
//                             ^ undefined or anything else is not gonna work

I think this should error, and a function should not be able to return a union that includes void. Perhaps only in strict mode.

There already is "Not all code paths return a value. ts(7030)"

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

No branches or pull requests

6 participants