TypeScript supports intersection types,5 improving expressiveness of the type system and reducing potential class hierarchy size, demonstrated as follows.
The following program code defines the classes Chicken, Cow, and RandomNumberGenerator that each have a method produce returning an object of either type Egg, Milk, or number. Additionally, the functions eatEgg and drinkMilk require arguments of type Egg and Milk, respectively.
The following program code defines the ad hoc polymorphic function animalToFood that invokes the member function produce of the given object animal. The function animalToFood has two type annotations, namely ((_: Chicken) => Egg) and ((_: Cow) => Milk), connected via the intersection type constructor &. Specifically, animalToFood when applied to an argument of type Chicken returns an object of type Egg, and when applied to an argument of type Cow returns an object of type Milk. Ideally, animalToFood should not be applicable to any object having (possibly by chance) a produce method.
Finally, the following program code demonstrates type safe use of the above definitions.
The above program code has the following properties:
The above minimalist example can be realized using inheritance, for instance by deriving the classes Chicken and Cow from a base class Animal. However, in a larger setting, this could be disadvantageous. Introducing new classes into a class hierarchy is not necessarily justified for cross-cutting concerns, or maybe outright impossible, for example when using an external library. Imaginably, the above example could be extended with the following classes:
This may require additional classes (or interfaces) specifying whether a produce method is available, whether the produce method returns food, and whether the produce method can be used repeatedly. Overall, this may pollute the class hierarchy.
The above minimalist example already shows that duck typing is less suited to realize the given scenario. While the class RandomNumberGenerator contains a produce method, the object randomNumberGenerator should not be a valid argument for animalToFood. The above example can be realized using duck typing, for instance by introducing a new field argumentForAnimalToFood to the classes Chicken and Cow signifying that objects of corresponding type are valid arguments for animalToFood. However, this would not only increase the size of the respective classes (especially with the introduction of more methods similar to animalToFood), but is also a non-local approach with respect to animalToFood.
The above example can be realized using function overloading, for instance by implementing two methods animalToFood(animal: Chicken): Egg and animalToFood(animal: Cow): Milk. In TypeScript, such a solution is almost identical to the provided example. Other programming languages, such as Java, require distinct implementations of the overloaded method. This may lead to either code duplication or boilerplate code.
The above example can be realized using the visitor pattern. It would require each animal class to implement an accept method accepting an object implementing the interface AnimalVisitor (adding non-local boilerplate code). The function animalToFood would be realized as the visit method of an implementation of AnimalVisitor. Unfortunately, the connection between the input type (Chicken or Cow) and the result type (Egg or Milk) would be difficult to represent.
On the one hand, intersection types can be used to locally annotate different types to a function without introducing new classes (or interfaces) to the class hierarchy. On the other hand, this approach requires all possible argument types and result types to be specified explicitly. If the behavior of a function can be specified precisely by either a unified interface, parametric polymorphism, or duck typing, then the verbose nature of intersection types is unfavorable. Therefore, intersection types should be considered complementary to existing specification methods.
A dependent intersection type, denoted ( x : σ ) ∩ τ {\displaystyle (x:\sigma )\cap \tau } , is a dependent type in which the type τ {\displaystyle \tau } may depend on the term variable x {\displaystyle x} .6 In particular, if a term M {\displaystyle M} has the dependent intersection type ( x : σ ) ∩ τ {\displaystyle (x:\sigma )\cap \tau } , then the term M {\displaystyle M} has both the type σ {\displaystyle \sigma } and the type τ [ x := M ] {\displaystyle \tau [x:=M]} , where τ [ x := M ] {\displaystyle \tau [x:=M]} is the type which results from replacing all occurrences of the term variable x {\displaystyle x} in τ {\displaystyle \tau } by the term M {\displaystyle M} .
Scala supports type declarations 7 as object members. This allows a type of an object member to depend on the value of another member, which is called a path-dependent type.8 For example, the following program text defines a Scala trait Witness, which can be used to implement the singleton pattern.9
The above trait Witness declares the member T, which can be assigned a type as its value, and the member value, which can be assigned a value of type T. The following program text defines an object booleanWitness as instance of the above trait Witness. The object booleanWitness defines the type T as Boolean and the value value as true. For example, executing System.out.println(booleanWitness.value) prints true on the console.
Let ⟨ x : σ ⟩ {\displaystyle \langle {\textsf {x}}:\sigma \rangle } be the type (specifically, a record type) of objects having the member x {\displaystyle {\textsf {x}}} of type σ {\displaystyle \sigma } . In the above example, the object booleanWitness can be assigned the dependent intersection type ( x : ⟨ T : Type ⟩ ) ∩ ⟨ value : x . T ⟩ {\displaystyle (x:\langle {\textsf {T}}:{\text{Type}}\rangle )\cap \langle {\textsf {value}}:x.{\textsf {T}}\rangle } . The reasoning is as follows. The object booleanWitness has the member T that is assigned the type Boolean as its value. Since Boolean is a type, the object booleanWitness has the type ⟨ T : Type ⟩ {\displaystyle \langle {\textsf {T}}:{\text{Type}}\rangle } . Additionally, the object booleanWitness has the member value that is assigned the value true of type Boolean. Since the value of booleanWitness.T is Boolean, the object booleanWitness has the type ⟨ value : booleanWitness.T ⟩ {\displaystyle \langle {\textsf {value}}:{\textsf {booleanWitness.T}}\rangle } . Overall, the object booleanWitness has the intersection type ⟨ T : Type ⟩ ∩ ⟨ value : booleanWitness.T ⟩ {\displaystyle \langle {\textsf {T}}:{\text{Type}}\rangle \cap \langle {\textsf {value}}:{\textsf {booleanWitness.T}}\rangle } . Therefore, presenting self-reference as dependency, the object booleanWitness has the dependent intersection type ( x : ⟨ T : Type ⟩ ) ∩ ⟨ value : x . T ⟩ {\displaystyle (x:\langle {\textsf {T}}:{\text{Type}}\rangle )\cap \langle {\textsf {value}}:x.{\textsf {T}}\rangle } .
Alternatively, the above minimalistic example can be described using dependent record types.10 In comparison to dependent intersection types, dependent record types constitute a strictly more specialized type theoretic concept.11
An intersection of a type family, denoted ⋂ x : σ τ {\textstyle \bigcap _{x:\sigma }\tau } , is a dependent type in which the type τ {\displaystyle \tau } may depend on the term variable x {\displaystyle x} . In particular, if a term M {\displaystyle M} has the type ⋂ x : σ τ {\textstyle \bigcap _{x:\sigma }\tau } , then for each term N {\displaystyle N} of type σ {\displaystyle \sigma } , the term M {\displaystyle M} has the type τ [ x := N ] {\displaystyle \tau [x:=N]} . This notion is also called implicit Pi type,12 observing that the argument N {\displaystyle N} is not kept at term level.
Barendregt, Henk; Coppo, Mario; Dezani-Ciancaglini, Mariangiola (1983). "A filter lambda model and the completeness of type assignment". Journal of Symbolic Logic. 48 (4): 931–940. doi:10.2307/2273659. JSTOR 2273659. S2CID 45660117. /wiki/Mariangiola_Dezani-Ciancaglini ↩
Palsberg, Jens (2012). "Overloading is NP-Complete". Logic and Program Semantics. Lecture Notes in Computer Science. Vol. 7230. pp. 204–218. doi:10.1007/978-3-642-29485-3_13. ISBN 978-3-642-29484-6. 978-3-642-29484-6 ↩
Henk Barendregt; Wil Dekkers; Richard Statman (20 June 2013). Lambda Calculus with Types. Cambridge University Press. pp. 1–. ISBN 978-0-521-76614-2. 978-0-521-76614-2 ↩
Ghilezan, Silvia (1996). "Strong normalization and typability with intersection types". Notre Dame Journal of Formal Logic. 37 (1): 44–52. doi:10.1305/ndjfl/1040067315. https://doi.org/10.1305%2Fndjfl%2F1040067315 ↩
"Intersection Types in TypeScript". Retrieved 2019-08-01. https://www.typescriptlang.org/docs/handbook/advanced-types.html#intersection-types ↩
Kopylov, Alexei (2003). "Dependent intersection: A new way of defining records in type theory". 18th IEEE Symposium on Logic in Computer Science. LICS 2003. IEEE Computer Society. pp. 86–95. CiteSeerX 10.1.1.89.4223. doi:10.1109/LICS.2003.1210048. /wiki/CiteSeerX_(identifier) ↩
"Type declarations in Scala". Retrieved 2019-08-15. https://www.scala-lang.org/files/archive/spec/2.12/04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases ↩
Amin, Nada; Grütter, Samuel; Odersky, Martin; Rompf, Tiark; Stucki, Sandro (2016). "The Essence of Dependent Object Types". A List of Successes That Can Change the World (PDF). Lecture Notes in Computer Science. Vol. 9600. Springer. pp. 249–272. doi:10.1007/978-3-319-30936-1_14. ISBN 978-3-319-30935-4. 978-3-319-30935-4 ↩
"Singletons in the Scala shapeless library". GitHub. Retrieved 2019-08-15. https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/singletons.scala ↩
Pollack, Robert (2000). "Dependently typed records for representing mathematical structure". Theorem Proving in Higher Order Logics, 13th International Conference. TPHOLs 2000. Springer. pp. 462–479. doi:10.1007/3-540-44659-1_29. /wiki/Doi_(identifier) ↩
Stump, Aaron (2018). "From realizability to induction via dependent intersection". Annals of Pure and Applied Logic. 169 (7): 637–655. doi:10.1016/j.apal.2018.03.002. https://doi.org/10.1016%2Fj.apal.2018.03.002 ↩
"C# Guide". Retrieved 2019-08-08. http://csharp.net/ ↩
"Discussion: Union and Intersection types in C Sharp". GitHub. Retrieved 2019-08-08. https://github.com/dotnet/csharplang/issues/399 ↩
"Eclipse Ceylon™". 19 July 2017. Retrieved 2023-08-16. https://projects.eclipse.org/projects/technology.ceylon/ ↩
"Intersection Types in Ceylon". 19 July 2017. Retrieved 2019-08-08. https://ceylon-lang.org/documentation/tour/types/#intersection_types ↩
"F# Software Foundation". Retrieved 2019-08-08. http://fsharp.org/ ↩
"Add Intersection Types to F Sharp". GitHub. Retrieved 2019-08-08. https://github.com/fsharp/fslang-suggestions/issues/600 ↩
"Flow: A Static Type Checker for JavaScript". Archived from the original on 2022-04-08. Retrieved 2019-08-08. https://web.archive.org/web/20220408204727/https://flow.org/en/ ↩
"Intersection Type Syntax in Flow". Retrieved 2019-08-08. https://flow.org/en/docs/types/intersections/#intersection-type-syntax ↩
Reynolds, J. C. (1988). Preliminary design of the programming language Forsythe. ↩
"Java Software". Retrieved 2019-08-08. https://www.oracle.com/java/ ↩
"IntersectionType (Java SE 12 & JDK 12)". Retrieved 2019-08-01. https://docs.oracle.com/en/java/javase/12/docs/api/java.compiler/javax/lang/model/type/IntersectionType.html ↩
"php.net". https://php.net ↩
"PHP.Watch - PHP 8.1: Intersection Types". https://php.watch/versions/8.1/intersection-types ↩
"The Scala Programming Language". Retrieved 2019-08-08. https://scala-lang.org/ ↩
"Compound Types in Scala". Retrieved 2019-08-01. https://www.scala-lang.org/files/archive/spec/2.12/03-types.html#compound-types ↩
"Intersection Types in Dotty". Retrieved 2019-08-01. http://dotty.epfl.ch/docs/reference/new-types/intersection-types.html ↩
"TypeScript - JavaScript that scales". Retrieved 2019-08-01. https://www.typescriptlang.org/ ↩
"Whiley: an Open Source Programming Language with Extended Static Checking". Retrieved 2019-08-01. http://whiley.org/ ↩
"Whiley language specification" (PDF). Retrieved 2019-08-01. http://whiley.org/download/WhileyLanguageSpec.pdf ↩