Self Modifying Type Predicates in Typescript

mpote.at
4 min read
fairly difficult
Typescript's type system is uniquely powerful among mainstream programming languages, approximating the expressive power of Haskell or Idris, while also remaining flexible enough for production applications. Type predicates are a useful tool in building a well-typed software framework. Essentially, they allow you to "simulate" dependents types, a powerful type feature present in Idris. Further explanation on type predicates can be found here. The premise of this article is a usage of type predicates I haven't seen discussed online, and thus an application I might consider "advanced" or at least somewhat obscure.
Typescript's type system is uniquely powerful among mainstream programming languages, approximating the expressive power of Haskell or Idris, while also remaining flexible enough for production applications.

Type predicates are a useful tool in building a well-typed software framework. Essentially, they allow you to "simulate" dependents types, a powerful type feature present in Idris.

Further explanation on type predicates can be found here.

The premise of this article is a usage of type predicates I haven't seen discussed online, and thus an application I might consider "advanced" or at least somewhat obscure.

Essentially, in the context of an interface or class , you may apply a type predicate which applies additional type constraints on this . To motivate the example, I'll invoke the common Shape class hierarchy, and try to avoid the corresponding quagmires with the Liskov substitution principle.

First, we will introduce the type ShapeTypes , which will be a mapping from the type 'circle' | 'rectangle' to Circle | Rectangle . Basically, this type converts a string type into a concrete Shape type.

Usually, every individual type would be in separate files.

// ShapeTypes.ts type ShapeTypes = { circle: Circle ; rectangle: Rectangle ; };

Next, we will define what it means exactly to be a Shape in this model. The magic here is the is abstract, generic function.

// Shape.ts abstract class Shape { public abstract get area() : number ; public abstract get perimeter() : number ; public abstract is < ShapeKey extends keyof ShapeTypes > ( shapeType: ShapeKey ) : this is ShapeTypes[ShapeKey]; }

Let's break down Shape , line-by-line:

The function declaration, specifying the class is abstract and therefore may possess abstract properties, and cannot be instantiated.

Instead of specifying Shape as an abstract class , this could be equivalently represented as an interface . However, the abstract class form is slightlymore extensible as we may more strictly specify…
About Michael Poteat, Software Engineer, Researcher In Sf. Interests In Machine Learning, Programming, Pure Mathematics, Many Other Topics.
Read full article