Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for the intersection type A & B #46

Merged
merged 4 commits into from
Oct 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions samples/intersectiontype.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
declare namespace intersectiontype {

type ArchiverOptions = CoreOptions & ExtraOptions & MoreExtraOptions;
type UnionOfIntersection = CoreOptions | CoreOptions & ExtraOptions | CoreOptions & MoreExtraOptions;
type Duplicates = CoreOptions & CoreOptions & ExtraOptions & MoreExtraOptions;

interface CoreOptions {
statConcurrency: number;
}

interface ExtraOptions {
allowHalfOpen: boolean;
}

interface MoreExtraOptions {
store: boolean;
}

export function test(v : CoreOptions & ExtraOptions): CoreOptions & MoreExtraOptions;

}
36 changes: 36 additions & 0 deletions samples/intersectiontype.ts.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

import scala.scalajs.js
import js.annotation._
import js.|

package intersectiontype {

package intersectiontype {

@js.native
trait CoreOptions extends js.Object {
var statConcurrency: Double = js.native
}

@js.native
trait ExtraOptions extends js.Object {
var allowHalfOpen: Boolean = js.native
}

@js.native
trait MoreExtraOptions extends js.Object {
var store: Boolean = js.native
}

@js.native
@JSGlobal("intersectiontype")
object Intersectiontype extends js.Object {
type ArchiverOptions = CoreOptions with ExtraOptions with MoreExtraOptions
type UnionOfIntersection = CoreOptions | CoreOptions with ExtraOptions | CoreOptions with MoreExtraOptions
type Duplicates = CoreOptions with ExtraOptions with MoreExtraOptions
def test(v: CoreOptions with ExtraOptions): CoreOptions with MoreExtraOptions = js.native
}

}

}
11 changes: 11 additions & 0 deletions src/main/scala/org/scalajs/tools/tsimporter/Importer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,17 @@ class Importer(val output: java.io.PrintWriter) {
TypeRef(QualifiedName.Function(params.size), targs)
}

case IntersectionType(left, right) =>
def visit(tpe: TypeTree, visited: List[TypeRef]): List[TypeRef] = {
tpe match {
case IntersectionType(left, right) =>
visit(left, visit(right, visited))
case _ =>
typeToScala(tpe) :: visited
}
}
TypeRef.Intersection(visit(tpe, Nil).distinct)

case UnionType(left, right) =>
def visit(tpe: TypeTree, visited: List[TypeRef]): List[TypeRef] = {
tpe match {
Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/org/scalajs/tools/tsimporter/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ object Trees {

case class UnionType(left: TypeTree, right: TypeTree) extends TypeTree

case class IntersectionType(left: TypeTree, right: TypeTree) extends TypeTree

case class TupleType(tparams: List[TypeTree]) extends TypeTree

case class TypeQuery(expr: QualifiedIdent) extends TypeTree
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class TSDefParser extends StdTokenParsers with ImplicitConversions {

lexical.delimiters ++= List(
"{", "}", "(", ")", "[", "]", "<", ">",
".", ";", ",", "?", ":", "=", "|", "*",
".", ";", ",", "?", ":", "=", "|", "&", "*",
// TypeScript-specific
"...", "=>"
)
Expand Down Expand Up @@ -190,10 +190,18 @@ class TSDefParser extends StdTokenParsers with ImplicitConversions {
":" ~> typeDesc

lazy val typeDesc: Parser[TypeTree] =
rep1sep(singleTypeDesc, "|") ^^ {
unionTypeDesc

lazy val unionTypeDesc: Parser[TypeTree] =
rep1sep(intersectionTypeDesc, "|") ^^ {
_.reduceLeft(UnionType)
}

lazy val intersectionTypeDesc: Parser[TypeTree] =
rep1sep(singleTypeDesc, "&") ^^ {
_.reduceLeft(IntersectionType)
}

lazy val singleTypeDesc: Parser[TypeTree] =
baseTypeDesc ~ rep("[" ~ "]") ^^ {
case base ~ arrayDims =>
Expand Down
14 changes: 14 additions & 0 deletions src/main/scala/org/scalajs/tools/tsimporter/sc/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ object Name {
val REPEATED = Name("*")
val SINGLETON = Name("<typeof>")
val THIS = Name("<this>")
val INTERSECTION = Name("<with>")
}

case class QualifiedName(parts: Name*) {
Expand Down Expand Up @@ -56,6 +57,7 @@ object QualifiedName {
def Function(arity: Int) = scala_js dot Name("Function"+arity)
def Tuple(arity: Int) = scala_js dot Name("Tuple"+arity)
val Union = scala_js dot Name("|")
val Intersection = QualifiedName(Name.INTERSECTION)
}

class Symbol(val name: Name) {
Expand Down Expand Up @@ -314,6 +316,18 @@ object TypeRef {
}
}

object Intersection {
def apply(types: List[TypeRef]): TypeRef =
TypeRef(QualifiedName.Intersection, types)

def unapply(typeRef: TypeRef): Option[List[TypeRef]] = typeRef match {
case TypeRef(QualifiedName.Intersection, types) =>
Some(types)

case _ => None
}
}

object Singleton {
def apply(underlying: QualifiedName): TypeRef =
TypeRef(QualifiedName(Name.SINGLETON), List(TypeRef(underlying)))
Expand Down
4 changes: 4 additions & 0 deletions src/main/scala/org/scalajs/tools/tsimporter/sc/Printer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ class Printer(private val output: PrintWriter, outputPackage: String) {
implicit val withPipe = ListElemSeparator.Pipe
p"$types"

case TypeRef.Intersection(types) =>
implicit val withWith = ListElemSeparator.WithKeyword
p"$types"

case TypeRef.This =>
p"this.type"

Expand Down