A topic in the Open Knowledge Graph — a free, open map of 15,290 topics and the order to learn them in.

Subtyping and Type Bounds

Graduate Depth 99 in the knowledge graph ☐ I know this ☆ Set as goal
2topics build on this
522prerequisites beneath it
See this on the map →
Type Systems OverviewConstraint-Based Type Checking+3 more→→Polymorphism and Type Variables
type-systems subtyping generics

Core Idea

Subtyping introduces a type ordering where subtypes are usable wherever supertypes are expected (Liskov substitution). Type bounds on generics (e.g., 'T extends Comparable') restrict which types can instantiate parameters, enabling safe polymorphic operations while maintaining type safety.

Explainer

From your study of type systems, you know that types classify values and that a type checker ensures operations are applied to compatible values. Subtyping extends this idea by introducing a relationship *between* types: if type S is a subtype of type T (written S <: T), then any value of type S can safely be used wherever a value of type T is expected. This is the Liskov Substitution Principle — a subtype must honor all the guarantees of its supertype.

The most familiar example is class inheritance in object-oriented languages. If `Dog` extends `Animal`, then `Dog <: Animal` — you can pass a `Dog` to any function expecting an `Animal`, because a `Dog` has every method and field that `Animal` has (and possibly more). But subtyping is broader than inheritance. In structural type systems (like TypeScript's), subtyping is determined by shape rather than declared hierarchy: if type A has all the fields that type B requires, A is a subtype of B regardless of whether A explicitly extends B. The key insight is that subtyping is about *capability guarantees* — a subtype can do everything the supertype can do.

Where subtyping gets subtle is with compound types, especially function types. If a function expects an `Animal` parameter and returns a `Dog`, what is the subtyping relationship between function types? Function types are contravariant in their parameters and covariant in their return types. This means a function `(Animal) → Dog` is a subtype of `(Dog) → Animal` — not the other way around. The logic is: a caller providing a `Dog` is safe if the function accepts any `Animal` (a broader input), and a caller expecting an `Animal` result is safe if the function always returns a `Dog` (a narrower output). Getting variance wrong is a common source of type-system unsoundness, and understanding it is essential for working with generic types.

Type bounds bring subtyping into the world of generics (parametric polymorphism). An unbounded generic type parameter `T` can be instantiated with any type, but then you cannot call any methods on values of type `T` — the type checker knows nothing about `T`'s capabilities. An upper bound like `T extends Comparable` constrains `T` to be a subtype of `Comparable`, so the compiler knows that values of type `T` have a `compareTo` method. This enables writing generic sorting functions, priority queues, and similar data structures that work with any comparable type while remaining fully type-safe. A lower bound like `T super Dog` constrains `T` to be a supertype of `Dog` — this is less common but important for safe write operations into generic collections.

If you have encountered the Hindley-Milner type system, you will notice that it achieves polymorphism through type variables and unification rather than subtyping. Adding subtyping to Hindley-Milner-style inference is notoriously difficult because subtyping introduces inequality constraints (S <: T) rather than equality constraints (S = T), making inference undecidable in the general case. This is why languages like Java and C# require explicit type annotations on class declarations and bounds on generics — they combine subtyping with limited inference rather than attempting full inference with subtyping. Understanding this tension between subtyping and inference is key to appreciating the design tradeoffs in real type systems.

Practice Questions 5 questions

Prerequisite Chain

Understanding Zero → The Number Zero → Counting to Five → Counting to 10 → Counting to 20 → Counting a Set of Objects Up to 20 → Cardinality: The Last Number Counted → Matching Numerals to Quantities → Subitizing Small Quantities → Addition Within 10 → Number Bonds to 10 → Addition Within 20 → Doubles and Near Doubles → Doubles Facts Within 10 → Near Doubles Facts Within 20 → Mental Math Strategies for Addition → Mental Math: Adding and Subtracting Tens → Addition Within 100 → Repeated Addition as Multiplication → Multiplication as Equal Groups → Multiplication: Arrays → Basic Multiplication Facts (0s, 1s, 2s, 5s, 10s) → Multiplication Facts Within 100 → Division as Equal Sharing → Division as Grouping (Measurement Division) → Division: Grouping (Repeated Subtraction) Model → Division: Fair Sharing Model → Division as Equal Sharing → Division as Grouping → Basic Division Facts → Division Facts Within 100 → Multiplication and Division Fact Families → Relationship Between Multiplication and Division → Division Facts as Inverse of Multiplication → Remainders and Quotients in Division → Division Word Problems → Multi-Step Word Problems → Solving Multi-Step Word Problems → Multiplication Word Problems → Division Word Problems → Introduction to Long Division → Factors and Multiples → Prime and Composite Numbers → Equivalent Fractions → Relating Fractions and Decimals → Decimal Place Value → Integers and the Number Line → Comparing and Ordering Integers → Absolute Value → Adding Integers → Subtracting Integers → Multiplying Integers → Introduction to Exponents → Order of Operations → Integer Order of Operations → Variable Expressions → The Distributive Property → Variables and Expressions Review → Introduction to Polynomials → Adding and Subtracting Polynomials → Multiplying Polynomials → Factorial → Permutations → Combinations → Counting Principles: Addition and Multiplication Rules → Introduction to Graph Theory → Propositional Logic Foundations → Logical Equivalences → Boolean Algebra → Boolean Type and Truth Values → Comparison Operators and Boolean Tests → Logical Operators and Boolean Algebra → Boolean Algebra and Fundamental Laws → Logic Gates Fundamentals → Implementing Boolean Functions with Gates → Karnaugh Map Simplification → Combinational Circuit Design → Flip-Flops and Latches → Finite State Machines (FSMs) → Deterministic Finite Automata (DFA) → Nondeterministic Finite Automata (NFA) → Two-Way Finite Automata → NFA to DFA Conversion (Subset Construction) → DFA Properties and Minimization Algorithms → Regular Languages: Definition and Characterization → Context-Free Grammars (CFGs) → Context-Free Grammar Properties and Ambiguity → Parse Trees, Derivations, and Ambiguity in CFGs → Context-Free Grammars in Compiler Design → Abstract Syntax Trees (ASTs) → Symbol Tables and Scope Resolution → Semantic Analysis Phase → Type Systems Overview → Unification Algorithm → Type Inference Algorithms → Hindley-Milner Type System → Bidirectional Type Checking → Constraint-Based Type Checking → Dependent Types and Value-Level Type Constraints → Subtyping and Type Bounds

Longest path: 100 steps · 522 total prerequisite topics

Prerequisites (5)

Leads To (1)