#Validating user defined type guard?

47 messages · Page 1 of 1 (latest)

digital marlin
#

Is there a way to create a type guard helper function that Internally does type validation?

type GreenBanana = { name: 'banana', color: 'green'}
type RedBanana = { name: 'banana', color: 'red'}
type Apple = { name: 'apple', color: 'green'}
type Fruit = GreenBanana | RedBanana | Apple

function isBanana(f: Fruit): f is RedBanana {
    return f.name === 'banana' // ok, should error saying that f.name could be RedBanana | GreenBanana
}
trail drum
#

You can use "assert type guards"

split whaleBOT
#
RogerC#3099

Preview:```ts
type GreenBanana = {name: "banana"; color: "green"}
type RedBanana = {name: "banana"; color: "red"}
type Apple = {name: "apple"; color: "green"}
type Fruit = GreenBanana | RedBanana | Apple

function isBanana(f: Fruit): asserts f is RedBanana {
if (f.name !== "banana") {
thr
...```

trail drum
#

My condition is bad in the code.. but you get the idea

#

Or do you mean something totally different?

solid drift
#

i don't think it's possible without doing it manually

split whaleBOT
solid drift
#

!ts

split whaleBOT
#
type GreenBanana = { name: 'banana', color: 'green'}
type RedBanana = { name: 'banana', color: 'red'}
type Apple = { name: 'apple', color: 'green'}
type Fruit = GreenBanana | RedBanana | Apple

function guard<T, U extends T>(fn: (t: T) => U | undefined): (t: T) => t is U {
  return (t: T): t is U => fn(t) !== undefined;
}

const isBanana = guard<Fruit, RedBanana>(f => f.name === 'banana' ? f : undefined);
//                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Type 'GreenBanana | RedBanana | undefined' is not assignable to type 'RedBanana | undefined'.
//   Type 'GreenBanana' is not assignable to type 'RedBanana'.
//     Types of property 'color' are incompatible.
//       Type '"green"' is not assignable to type '"red"'.```
solid drift
#

you'd still have to be pretty careful to only return either the argument or undefined

#

but it's better than nothing

#

(@digital marlin)

digital marlin
#

@solid drift My goal is to encapsulate complex type checking into a function, but in a way that doesn't cause me to lose compile time checks.

#

In this trivial example I can use a generic, but the general idea here is that the guard may be a large complex function for type checking and I am specifically trying to move that code out of my primary code path.

solid drift
#

yeah type guards are just not typechecked whatsoever

#

i'm not quite sure what other alternatives would be

#

it'd probably be possible to add a type-driven lint rule? 🤔

#

not sure whether it would be worth the effort, of course

digital marlin
#
if (typeof x === 'object' && x !== null && !Array.isArray(x) && 'prop' in x && x === 'apple') { x.prop // 'apple' }
// vs
if (isAppleHolder(x)) { x.prop // 'apple' }
#

I can create isAppleHolder with an is guard clause, but I lose actual compiler checking with that. 😢

solid drift
#

yeah :(

solid drift
solid drift
digital marlin
#

Ah! You are leveraging the inferred type of the function returned by guard to do the type checking for you. Clever!

#

Thanks @solid drift!

#

!resolved

solid drift
#

but upon further thought

split whaleBOT
solid drift
#

!ts

split whaleBOT
#
type GreenBanana = { name: 'banana', color: 'green'}
type RedBanana = { name: 'banana', color: 'red'}
type Apple = { name: 'apple', color: 'green'}
type Fruit = GreenBanana | RedBanana | Apple

function guard<T, U extends T>(fn: (t: T) => U | undefined): (t: T) => t is U {
  return (t: T): t is U => fn(t) !== undefined;
}

const isBanana = guard((f: Fruit) => f.name === 'banana' ? f : undefined);
//    ^? - const isBanana: (t: Fruit) => t is GreenBanana | RedBanana```
solid drift
#

it does indeed work with inference

digital marlin
#

What are the constraints of the function passed to guard?

#

That is, what does the author need to guarantee in order for the type checking to work?

#

Just need to make sure it returns undefined in the "failure" code path, and a narrowed fruit in all others?

digital marlin
#

I don't get this error in 4.9.5.

#

Oh, I see.

#

This new version just creates a guard clause with the types it guards against, without you needing to specify generic parameters. Nice!

#

Thanks again @solid drift!

#

This should be part of the TS standard library.

#

Super useful.

solid drift
solid drift
solid drift
digital marlin
#

Hmm, good point. In my head for some reason I was treating this as purely a compile time thing, but it is totally a runtime check.