question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using union types together with literal singleton types

See original GitHub issue

This is a follow up on a gitter conversation with @smarter

I’m getting accustomed to some very handy uses of union types and literal singleton types in the Typescript part of our application. I’ve been trying this and some other TS uses of union types my beloved Scala with Dotty.

Things don’t seem to work quite yet.

The most useful one is using union types with literal singleton types.
At the moment that combination doesn’t seem to work very much.

That’s a pity because there’s at least one use case that’s very useful. It’s possible to encode enumerations to constrain function arguments and assignments.

type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9   // seems to be interpreted as type Int

val digits: List[Digit] = List(-1, 0, 11)   // this compiles right now. 

It’s something that has just appeared in Typescript 2.0 and it’s really really useful.

I understood from @smarter that there’s a concern around the performance of inferring return types:

The main issue is that you can’t always preserve singleton types and unions, otherwise any complex if/else if/…/else will end up having a type like 10 | 32 | 50 | …, doing subtyping checks on huge union types like this is likely to slow down the compiler my idea is that we should preserve unions of singleton types and not widen them only if the expected type is a union

Another feature I use a lot in Typescript is working with non nullable types. Instead of using an Option[A] you use a A | Null kind construction. Using a compiler flag or language feature import it would be possible to disallow null asignment.

val foo: String = null // wouldn't compile

val bar: String | Null = null  // would compile

Currently the Null type seems to be thrown away. Here’s the relevant TS doc
I think it’s cool to have an alternative for the Option monad. But if it’s hard to implement I’m perfectly fine with using Option[A].

Invoking a function on an object with a union type doesn’t work right now with Dotty

class Bird() {
    def fly()={}
    def layEggs()={}
}

class Fish() {
    def swim()={}
    def layEggs()={}
}

def getSmallPet(): Fish | Bird = if(new Random().nextBoolean) new Fish() else new Bird()

val pet = getSmallPet()   // returns Object right now
pet.layEggs() // This should compile shouldn't it?
pet.swim();    // and this shouldn't 

The example is from Typescript docs
I guess this would be mostly useful for Scalajs since its compile target is very duck typing oriented.

For me and the Scala developers I’ve spoken to its the combination of union types and literal singleton types that’s most valuable.

Btw I was amazed how quickly I got a response on gitter. On a Sunday! 😃

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:1
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
smartercommented, Sep 5, 2016
  1. Union types with singleton types is something I’ll try to look at in the next few weeks.
  2. Currently Null is a subtype of every non-value class, non-nullable types are on our roadmap but require a substantial amount of work, especially if you want smooth interoperability with Java/Scala2
  3. Your example works if you replace val pet = getSmallPet() with val pet: Fish | Bird = getSmallPet() or alternatively if you put import scala.language.keepUnions at the top of your file (note: I’m pretty sure this should be import dotty.language.keepUnions, we seem to have a bug here). Currently we never infer union types without the keepUnions import but your example illustrates a case where we might want to relax this rule, this might be hard to do since we would need to precisely keep track of the source of the inferred type.
0reactions
joost-de-vriescommented, May 3, 2019

Fixed by #6299 🎉

Read more comments on GitHub >

github_iconTop Results From Across the Web

Handbook - Unions and Intersection Types - TypeScript
A common technique for working with unions is to have a single field which uses literal types which you can use to let...
Read more >
Using TypeScript's singleton types in practice - Medium
Discriminated unions​​ We have seen how a union of singleton types help writing safer conditional statements. The idea above can then be extended ......
Read more >
Typescript union type with singleton types does not compile
The problem is the object literal type above is compatible with neither of the union members so we end up with an error....
Read more >
Scala 3 Unions: Simulating Dynamic Typing with Union Types
The ability to create your own types using the combined power of literal types and union types gives you more flexibility to craft...
Read more >
Advanced Types - TypeScript - JavaScript that scales.
A union type describes a value that can be one of several types. We use the vertical bar ( | ) to separate...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found