Type Systems

Describes the rules and behaviors for types in a language/system.

Rules

Rules which should be outlined by a type system.

Types Definition

Defining custom types.

Literal Type

A literal type is a value, enforced as a type.

That means, a variable of literal type can only have value of the literal and nothing else.

You can say it is a subtype of a primitive data type. While primitive data types allow a range of values, literal narrows it down to specific predefined value (values in some cases).

For example:

let iamlit: "Hello"; //"Hello" is literal type
iamlit = "Hello"; //OK
iamlit = "Bounjor" //Error

Here iamlit can only have value of the literal.

Enum

(Enumerated Types)

Data type consisting of a set of named values called elements, members, enumeral, or enumerators of the type. The enumerator names are usually identifiers that behave as constants in the language.

Usage

Where fixed set of constants or ranges are required

Examples
  • the four suits in a deck of playing cards may be four enumerators named Club, Diamond, Heart, and Spade, belonging to an enumerated type named suit. If a variable V is declared having suit as its data type, one can assign any of those four values to it.
  • range of phones/laptops, colors, seasons, days of week.
Isn't it #Literal Type?

While enums, like literal limit the set of values possible for a variable, they are not literal. Enums group together #Literal Types and give them semantic meaning.

Union Types

Combines several types into one.

Represents value that can be of any of the constituents types.

Example:

let id: number | string

id can be either number or string.

Intersection Types

Combines several types into one.

Represents value that can be both of the constituents types.

Type Aliasing

Providing custom names for existing types.

Type Polymorphism

Ability of a programming language to write code which can work for multiple types.

  • Parametric Polymorphism (Generic Programming)
  • Ad-hoc Polymorphism (Function Overloading/Operator Overloading)
  • Subtype Polymorphism (Inheritance/Interfaces)

Casting / Type Conversion

Casting is the conversion of variable/object from one data type to another.

Depending upon the entity it is being performed on or the way it is being performed it can be classified as follows:

Classification based on casting syntax

Explicit Casting

  • When you have to specify that you're converting the variable/object intentionally and you don't have any problem with loosing information in your variable or making your object more specific.
  • #Narrowing Conversion and #Downcasting.

Implicit Casting

  • When you don't need to specify that you need some variable/object to be converted. The language takes care of it automatically.
  • #Widening Conversion and #Upcasting.
  • aka Implicit Type Conversion
  • aka Implicit Type Coercion, used in slightly different context. "coercion" is often used when the automatic change happens in more implicit, possibly lossy ways, especially in dynamic languages.

Classification based on entity being casted

Primitive Casting

When conversion is performed on primitive data types.

Narrowing Conversion
Widening Conversion
  • When we convert from a smaller/simpler data type to a larger one.
  • Generally, #Implicit Casting.

Object Type Casting

  • Similarity with primitive conversion => Converting from one type to another

  • Difference

    Primitive type variables store values. So when we convert from a larger to type to smaller type, we might end up loosing information.

    Reference variables on the other hand do not contain the object itself, but its reference. So when we convert types of objects, we're not changing the object, but we're just changing the label on the object, expanding or narrowing the opportunities to work with.

    Upcasting narrows the list of methods and properties available to this object and down casting can extend it.

Upcasting

Casting from subclass to superclass

Syntax

Generally, #Implicit Casting. Why? => Compiler knows that Cat is an Animal. OO Design Principles#Liskov Substitution Principle

What upcasting gives us?
  • OOP#Polymorphism

    Instead of using different methods for a common functionality in each of the sub classes, we can have a common method in the superclass for that functionality and all subclass objects will be casted implicitly.

    Example - a feed(Animal animal) function to which we can pass objects of Cat and Dog classes → feed(cat); feed(dog);

  • OOP#Overriding

    When an object is upcasted, it still can call overridden methods from its original class.

    For example, if Animal class has an eat() method and subclasses Cat and Dog override it.

    eat(animal) will call the methods from either the class Cat or Dog, whichever it was before upcasting.

    OOP#Runtime Polymorphism is a result of Upcasting.

Downcasting

Casting from superclass to subclass

Syntax

Generally, #Explicit Casting and same as #Narrowing Conversion of primitive types.

Beware of Issues#Class Cast Exception.

Characteristics

Type Safety

Defines what kind of conversions are allowed, more specifically in case of incompatible types. For example, int to string.

Strong Typing

Strongly-typed systems don't allow #Implicit Casting of incompatible types. You have to explicitly state that you want to convert the type.

Weak Typing

Weak-types systems try to convert different types on its own. You don't have to specify when assigning a value of one type to another incompatible type.

Type Inference

Ability of a system to infer types without them being explicitly annotated.

Type Checking

Defines the stage at which the types are validated.

Static Type Checking

Types checked at compile time.

Dynamic Type Checking

Types checked at runtime.

Type Compatibility

Nominal Typing (Name-based Typing)

Two types are only same/compatible if they share common name or inheritance hierarchy.

Structural Typing (Structure-based Typing)

Two types are compatible if they share same structure (properties and methods), regardless of their names.

Type Relations

Subclassing

Parent's code can be shared by subclasses.

Substitutability

We can replace an object of Animal type with object of Cat type.

More formally — OO Design Principles#Liskov Substitution Principle.

Subtyping

A set of classes confirming to a certain interface signature which can override certain parts of the signature with their own implementations.

Variance

Variance defines how the type relations of complex types will vary or hold in their components( entities and components in programming languages like enumerables (enums), functions, arrays, lists etc).

We know how #Substitutability works in case of objects. This kind of replacement can also be done with the components of these types. This is called variance.

  • When objects are replaced — substitutability
  • When components are replaced — variance

For example, a type Cat and Animal, where Animal is parent (or whatever) of Cat. Variance defines how the subtypes of Cat and subtypes of Animal will either hold or deviate from this relation.

The relation is:

  • variant if the relation is somehow carried on to the components or children, either holding the relation or reversing the relation.
  • covariant if it preserves the ordering of types.
  • contravariant if it reverses this order. ^105062
  • bivariant if both of the above apply
  • invariant or nonvariant if the relation is not carried on to the components or children. ^b36f21
Thoughts on deciding variance

While designing any type system, variance is considered by the designer.

  • Contravariance is usually considered unintuitive by programmers. This can lead to complex typing rules.
  • If the type system is variant, the type system is considered to be well-typed.
  • Sometimes the designer will choose to keep it invariant to keep the type system simple. But this could voilate type-safety.

References

© 2025 All rights reservedBuilt with Flowershow Cloud

Built with LogoFlowershow Cloud