简体   繁体   中英

Is it OK to cast undefined to void in TS?

TLDR;

Is this OK? Or is it bad practice?

function isUndefined (payload: any): payload is undefined | void {
  return payload === undefined
}

Context

In TypeScript, I have a function that can return either something or undefined or void .

Something like an event handler that can return a modified payload or the dev can choose to return nothing or undefined in case they won't modify the payload:

function eventHandler <T extends {[key: string]: any}> (payload: T): Modified<T> | undefined | void {
  // ... implementation
}

Then I have a type checker that needs to check if it's returning something other than void or undefined:

const result = eventHandler(payload)

if (result !== undefined) {
  // we have a modified payload!
}

However, is the above snippet I get an error because it says that even though result !== undefined it can still be void ?

In my opinion, I think it's peculiar because void should be the same as undefined .

So I made this type checker that solves it:

function isUndefined (payload: any): payload is undefined | void {
  return payload === undefined
}

This solves my issue, but my question is:

Is this OK? Or is it bad practice?

void is not undefined . void means the absence of a return value. undefined is the type of the value undefined at runtime.

it is true that a function that returns no value at run time returns undefined , but in the TS type system we chose to make the absence of a return value special.

For example assigning (a) => void to (a) => number | undefined (a) => void to (a) => number | undefined is likely an error, though it is safe at run-time.

In general do not use void except in the return type of functions. for everything else, use undefined .

So, yes we will need to use different check for undefined and void .

I think you're making this more complicated that it needs to be. A function that returns void can:

  1. Have no return statement
  2. Have a return; statement with no value specified.
  3. Have a return undefined; statement.

In pure javascript, all of the above would have a return value of undefined . If you say a function returns undefined , then you can only do #2 and #3 from the above list.

So you can just have a function type that unions void with whatever something you want.

function foo(): string | void {
    return Math.random() > 0.5 ? 'abc' : 123
}

const val = foo()
if (val === undefined) {
    console.log('is undefined', val)
} else {
    console.log('is some value', val)
}

This means that you could create a generic function type that modifies payloads like so:

type PayloadModifier<T extends {[key: string]: any}> = (payload: T) => T | void

const setMaxAsTen: PayloadModifier<{a: number}> = (payload) => {
    if (payload.a > 10) {
        return { a: 10 }
    }
    return undefined // required unless noImplicitReturns is false
}

const val = setMaxAsTen({a: 5})
if (val === undefined) {
    console.log('is undefined', val)
} else {
    console.log('is some value', val)
}

Playground

Last thing to note is that there is a compiler option that is good to leave on called noImplicitReturns . if a function declares a return value in any execution branch, then a return value must be declared in every execution branch. So because the above sometimes returns a value, you must explicitly return undefined if you don't to return a payload. You can toggle off that option, allowing you to omit that line, but it's not recommended as it does help you catch some bugs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM