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 morePolymorphism 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 ZeroThe Number ZeroCounting to FiveCounting to 10Counting to 20Counting a Set of Objects Up to 20Cardinality: The Last Number CountedMatching Numerals to QuantitiesSubitizing Small QuantitiesAddition Within 10Number Bonds to 10Addition Within 20Doubles and Near DoublesDoubles Facts Within 10Near Doubles Facts Within 20Mental Math Strategies for AdditionMental Math: Adding and Subtracting TensAddition Within 100Repeated Addition as MultiplicationMultiplication as Equal GroupsMultiplication: ArraysBasic Multiplication Facts (0s, 1s, 2s, 5s, 10s)Multiplication Facts Within 100Division as Equal SharingDivision as Grouping (Measurement Division)Division: Grouping (Repeated Subtraction) ModelDivision: Fair Sharing ModelDivision as Equal SharingDivision as GroupingBasic Division FactsDivision Facts Within 100Multiplication and Division Fact FamiliesRelationship Between Multiplication and DivisionDivision Facts as Inverse of MultiplicationRemainders and Quotients in DivisionDivision Word ProblemsMulti-Step Word ProblemsSolving Multi-Step Word ProblemsMultiplication Word ProblemsDivision Word ProblemsIntroduction to Long DivisionFactors and MultiplesPrime and Composite NumbersEquivalent FractionsRelating Fractions and DecimalsDecimal Place ValueIntegers and the Number LineComparing and Ordering IntegersAbsolute ValueAdding IntegersSubtracting IntegersMultiplying IntegersIntroduction to ExponentsOrder of OperationsInteger Order of OperationsVariable ExpressionsThe Distributive PropertyVariables and Expressions ReviewIntroduction to PolynomialsAdding and Subtracting PolynomialsMultiplying PolynomialsFactorialPermutationsCombinationsCounting Principles: Addition and Multiplication RulesIntroduction to Graph TheoryPropositional Logic FoundationsLogical EquivalencesBoolean AlgebraBoolean Type and Truth ValuesComparison Operators and Boolean TestsLogical Operators and Boolean AlgebraBoolean Algebra and Fundamental LawsLogic Gates FundamentalsImplementing Boolean Functions with GatesKarnaugh Map SimplificationCombinational Circuit DesignFlip-Flops and LatchesFinite State Machines (FSMs)Deterministic Finite Automata (DFA)Nondeterministic Finite Automata (NFA)Two-Way Finite AutomataNFA to DFA Conversion (Subset Construction)DFA Properties and Minimization AlgorithmsRegular Languages: Definition and CharacterizationContext-Free Grammars (CFGs)Context-Free Grammar Properties and AmbiguityParse Trees, Derivations, and Ambiguity in CFGsContext-Free Grammars in Compiler DesignAbstract Syntax Trees (ASTs)Symbol Tables and Scope ResolutionSemantic Analysis PhaseType Systems OverviewUnification AlgorithmType Inference AlgorithmsHindley-Milner Type SystemBidirectional Type CheckingConstraint-Based Type CheckingDependent Types and Value-Level Type ConstraintsSubtyping and Type Bounds

Longest path: 100 steps · 522 total prerequisite topics

Prerequisites (5)

Leads To (1)