type-guards icon indicating copy to clipboard operation
type-guards copied to clipboard

Allow missing properties when `undefined` would be a valid value?

Open lazarljubenovic opened this issue 4 years ago • 1 comments

For a majority of usecases, the following types are treated as equal:

interface Type1 {
  x?: number
}

interface Type2 {
  x: number | undefined
}

The difference that Type2 requires x to be present on the object, while Type1 allows the object to be empty. There's no difference when accessing type.x -- the inherited type of such expression would be number | undefined in both cases.

However, the observable difference occurs when iterating over keys. Object.keys(type) would in first case return either [] or ['x'], while in the second case it's guaranteed that the result would be ['x'], exclusively.

type-guards currently cannot handle the optional argument right now, forcing the developer to write things like this.

const isType = tg.isOfShape({
  x: tg.fp.or(tg.isUndefined, tg.isNumber),
})

type Type = tg.FromGuard<typeof isType>

The issue here is that Type would be same as Type2, while the current behavior of the library is that the run-time guard is the same as Type1 (x is required to exist on the object, even if its value is undefined).

Maybe it would be a good idea for both the runtime guard and the static type to result in the type Type1 | Type2. In cases when a key is required to exist (but its value is allowed to be undefined), the developer would need to make additional runtime checks, without option to rely on static types. I'm not sure yet if this is a good compromise.

lazarljubenovic avatar Aug 17 '19 09:08 lazarljubenovic