#How to create a falsy type Pending that behaves like undefined

1 messages · Page 1 of 1 (latest)

regal dragon
#

Hey everyone. I am experienced with JavaScript but now getting into Typescript. Still trying to wrap my head around the whole thing.

I am creating a card game server. A particular type that I want is called Pending — to be used for scores and trump suits. For example, the Trump suit of a particular round will either be of type (enum) Suit, or it will be Pending (before it's dealt or otherwise decided).

I basically want to create a type (or a "value", I guess??) of Pending. So for example, when I create a new Round, it will be like:

interface Round = {
  startingPlayer: number
  trumpSuit: Suit | Pending
}
const round: Round = {
  startingPlayer: 1,
  trumpSuit: Pending
}

I also want this Pending thing to be falsy, so that I can make checks on it like if(!trumpSuit).
However, if I define Pending to be a type alias of undefined, TypeScript will give me the following error:

type Pending = undefined
const myVar = Pending // error: Pending is a type, but is referred to as a value here

How do I achieve what I want to achieve? I know that I'm probably misusing the idea of type aliases or literals. But basically I want to be able to pass a value of Pending (that underneath is just undefined or null), but a variable that is created as undefined shouldn't pass the type check of Pending, e.g

const round: Round = {
  startingPlayer: 1,
  trumpSuit: undefined // I want an error saying "can't be undefined. Must be Suit or Pending.

Any advice on how I should approach this? Should I just use a string literal of "pending"? In that case, how would I achieve the desired falsy behavior?

#

I found this, which is maybe addressing what I'm trying to find. How do I create a new 'version' of something like undefined that is passed around differently. Am I just trying to create a new primitive type?

slim summit
#

typescript has what's called a "structural type system", which treats all types that have the same shape as the same type

#

so there's no true way to create a "new subtype" of an arbitrary type

#

for most types, it's as simple as adding an additional property, i.e.

type StringSubtype = string & { someBrand: true }

there, StringSubtype is assignable to string but string is not assignable to StringSubtype

#

unfortunately, null and undefined cannot have properties so there is no way to make a new subtype of those two types

#

the common workaround is to use a discriminated union

trumpSuit: { type: "suite", suite: Suite } | { type: "pending" }
#

another example of the structural type system in action is

class Foo {
    bar: string;
    baz: number;
}

const x: Foo = { bar: "", baz: 1 } // no error, the types have the same shape
#

it doesn't matter that one's a class instance and the other isn't

regal dragon
#

Thank you for the info!! This is quite helpful. I will look into discriminated unions now (at a first glance it seems to solve a few problems that I've been having).

#

@slim summit Thank you! I will see how I can incorporate this with the Trump Suit logic, but it's already super helpful for other areas.

#

I have a question — would you recommend I use the discriminated union (adding a bit more overhead but being safe) for this usecase, or to pass in undefined without my dream-world subtype of pending — i.e making the assumption that if trumpSuit is undefined, it's because I explicitly named it as such.

slim summit
#

honestly, I just use undefined (or sometimes null, because it doesn't disappear when converted to json)

#

I'd only stop using undefined if I needed to be very explicit about the state

#

or if I needed multiple "null" states

#

but the overhead of using a discriminated union is most likely minimal in comparison to the rest of your code

#

if you're recreating these objects 10,000 times in a loop, then maybe the overhead might be meaningful

regal dragon
#

👍