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

Is there a way for an object predicate to differentiate between key with value undefined, and key not present? #234

Open
atomanyih opened this issue Mar 10, 2022 · 2 comments

Comments

@atomanyih
Copy link

If we want to validate an object with optional parameters for a spread:

type Hotdog = {
  length: number;
  topping: string;
}

function patchHotdog(hotdog: Hotdog, patchBody: unknown): Hotdog {
  ow(
    patchBody,
    ow.object.exactShape({
      length: ow.optional.number,
      topping: ow.optional.string,
    })
  )
  
  return {
    ...hotdog,
    ...patchBody
  }
  // ERROR:
  //TS2322: Type '{ length: number | undefined; topping: string | undefined; }' is not assignable to type 'Hotdog'.
  //   Types of property 'length' are incompatible.
  //     Type 'number | undefined' is not assignable to type 'number'.
  //       Type 'undefined' is not assignable to type 'number'.
}

This doesn't work, as ow will assert patchBody as

type ValidatedPatchBody = {
  length: number | undefined;
  topping: string | undefined;
}

when really we want

type ValidatedPatchBody = {
  length?: number;
  topping?: string;
}

Which makes a difference, as js treats undefined value and no key differently when spreading:

const obj1 = { bar: 'foo', ...{} } // => { bar: 'foo' }
const obj2 = { bar: 'foo', ...{ bar: undefined } } // => { bar: undefined }

Unless I'm missing something, it doesn't look like ow provides a way to do this out of the box.


In the mean time, I've worked around this by passing in an explicit type argument:

function patchHotdog(hotdog: Hotdog, patchBody: unknown): Hotdog {
  ow<{length?: number, topping?:string}>(
    patchBody,
    ow.object.exactShape({
      length: ow.optional.number,
      topping: ow.optional.string,
    })
  )

  return {
    ...hotdog,
    ...patchBody
  } // IT OKAY
}
@sindresorhus
Copy link
Owner

Unless I'm missing something, it doesn't look like ow provides a way to do this out of the box.

Correct. .optional indicates the value is optional, not the key.

@sindresorhus
Copy link
Owner

I think this use-case should be supported, but I don't have a clear idea on how right now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants