#Infer possible property values in a generic type guard function.

24 messages · Page 1 of 1 (latest)

feral kettle
#

Hi everyone

I implemented a type guard for types that contain the type property. I use this property to identify objects such as:

type UserSignIn = { type: 'user-sign-in' };
type UserSignOut = { type: 'user-sign-out' };

type UserAction = UserSignIn | UserSignOut

I then write predicate functions that infer the object properties:

export const isUserSignIn = (action: UserAction) => isTypedOf<UserSignIn>(action, 'user-sign-in');

Here's how I've defined the type guard function:

/**
 * An object that contains a property named 'type'. This property uniquely identifies a type among
 * other types that also contain the same property.
 */
type TypedObject = { type: string };

/**
 * Type guard for objects that represent their types via the `type` property.
 *
 * @param value - the object being type guarded
 * @param type - the name of the type that matches the object
 */
export function isTypedOf<T extends TypedObject>(value: TypedObject, type: string): value is T {
    return value.type === type;
}

Everything works great at runtime, but it's a painful writing the predication functions because the IDE doesn't suggest me the value of the type.
Can someone help me rewrite the type guard function so that the IDE is able to infer the possible type value and suggest it?

Thank you,
Freitas

placid shoal
#

would type: TypedObject["type"] work for you?

#

if you make that the generic, then you don't need to provide the generic explicitly either, you can set the predicate as T & { type: U } or Extract<T, { type: U }>

feral kettle
placid shoal
#

oh wait i mistyped, sorry i meant T["type"]

#

that works

#

mb

modern coyoteBOT
#
that_guy977#0

Preview:```ts
type UserSignIn = { type: 'user-sign-in' };
type UserSignOut = { type: 'user-sign-out' };

type UserAction = UserSignIn | UserSignOut

/**

  • An object that contains a property named 'type'. This property uniquely identifies a type among
  • other types that also contain the same property.
    ...```
modern coyoteBOT
#
that_guy977#0

Preview:```ts
...
export function isTypedOf<
T extends TypedObject,
U extends T["type"]

(value: T, type: U): value is T & {type: U} {
return value.type === type
}
...```

placid shoal
#

it would be safer this way

#

also, by wrapping the predicate in a plain function, you lose the narrowing behavior of the predicate, so i'd recommend not doing that

feral kettle
#

Thanks, that made the trick! Now I have another issue
I don't have explicit types, instead I have functions and declare types using ReturnType

For example:

export function UserSignIn() {
    return { type: 'user-sign-in' }
} 

type UserSignIn = ReturnType<typeof UserSignIn>

With types defined like this, the IDE doesn't suggest the value. Do you know if there's a way to make it work?

placid shoal
#

did you check what UserSignIn is?

#

looks like it'd be string here

#

add as const after the string literal or the object literal to retain the literal type
(the latter would also make the property readonly)

feral kettle
#

Thank you so much, as const did the trick ❤️

placid shoal
#

if you want to use a wrapper function then you also have to make it a predicate

#

you could instead have a function that returns the proper predicate for you

#

but then you'd have one that could take any TypedObject rather than just UserAction, unless you want to specify the whole type argument list

#

i'd personally just not use a wrapper