Hone logo
Hone
Problems

Type-Level Factorial Calculation with Recursion

Type-level programming in TypeScript allows us to perform computations at compile time, enabling powerful type-level abstractions. This challenge focuses on implementing a factorial function using type-level recursion. Successfully completing this challenge demonstrates an understanding of conditional types and recursive type definitions, crucial for advanced TypeScript type manipulation.

Problem Description

You are tasked with creating a type Factorial that calculates the factorial of a number represented by a type. The type Factorial<N> should resolve to a type representing the factorial of N. The factorial is defined as:

  • Factorial<0> = 1
  • Factorial<N> = N * Factorial<N-1> for N > 0

This requires a recursive type definition using conditional types. The base case is Factorial<0> = 1, and the recursive step involves multiplying the input type N with the result of Factorial<N-1>. We'll represent numbers as types using distributive conditional types. The type Positive<N> will represent a positive integer.

Key Requirements:

  • The solution must be purely type-level; no runtime code is involved.
  • The solution must correctly calculate the factorial for non-negative integer types.
  • The solution should be well-typed and avoid type errors.
  • The solution should be reasonably efficient in terms of type complexity (avoiding unnecessary type expansions).

Expected Behavior:

The type Factorial<N> should resolve to a type representing the factorial of N. For example, Factorial<3> should resolve to a type representing 6.

Edge Cases to Consider:

  • Factorial<0> should resolve to 1.
  • The solution should handle larger numbers (within the limits of TypeScript's type system). While extremely large factorials might cause type errors due to complexity, the solution should work correctly for reasonable values.
  • Negative numbers are not defined for factorial. The challenge does not explicitly require error handling for negative inputs, but the solution should not produce a valid factorial for negative numbers.

Examples

Example 1:

type Factorial<N extends number> = 1 extends N ? 1 : N extends 0 ? 1 : N extends 1 ? 1 : N extends 2 ? 2 : N extends 3 ? 6 : N extends 4 ? 24 : N extends 5 ? 120 : N extends 6 ? 720 : N extends 7 ? 5040 : N extends 8 ? 40320 : N extends 9 ? 362880 : N extends 10 ? 3628800 : never;

type Result1 = Factorial<0>; // 1
type Result2 = Factorial<3>; // 6
type Result3 = Factorial<5>; // 120

Explanation: Factorial<3> resolves to 6 because the conditional type checks if N is 3, and if so, returns 6.

Example 2:

type Positive<N extends number> = N extends 0 ? never : N;

type Result4 = Factorial<Positive<5>>; // 120

Explanation: Positive<5> ensures that the input is positive. The Factorial type then correctly calculates the factorial of 5.

Constraints

  • The input N will be a number type.
  • The solution should be able to handle factorials up to Factorial<10> without causing excessive type errors or performance issues. Going beyond this might lead to type system limitations.
  • The solution must be written in TypeScript.
  • The solution should be as concise and readable as possible while maintaining correctness.

Notes

  • Consider using distributive conditional types to handle the recursive step.
  • The Positive<N> type is provided to ensure that only positive numbers are used as input. You can use it to add an extra layer of type safety.
  • The never type can be used to represent undefined or invalid cases.
  • TypeScript's type system has limitations on the complexity of type expressions. Extremely large factorials might exceed these limits.
  • Think about how to structure the conditional type to handle the base case and the recursive step effectively. The order of the conditions matters.
Loading editor...
typescript