Yuzhe's Blog

yuzhes

EndsWith

EndsWith

Problem Link

Problem

Implement EndsWith<T, U> which takes two string types and returns whether T ends with U.

type R0 = EndsWith<'abc', 'bc'>  // true
type R1 = EndsWith<'abc', 'abc'> // true
type R2 = EndsWith<'abc', 'd'>   // false

Solution

Approach 1: Template Literal Pattern Match

Use a template literal to check if the string ends with U by matching ${string}${U}:

type EndsWith<T extends string, U extends string> =
  T extends `${string}${U}` ? true : false

How it works:

This is the idiomatic, minimal TypeScript solution and covers all edge cases:

Approach 2: Recursive Suffix Stripping

An alternative (more verbose) approach: recursively check character by character from the end.

type EndsWith<T extends string, U extends string> =
  U extends ''
    ? true
    : T extends `${infer _}${U}`
      ? true
      : false

This is equivalent to Approach 1 but spells out the empty-string base case explicitly.

Approach 3: Reverse and StartsWith

If you already have a StartsWith helper (e.g., from challenge 2688), you can reverse both strings:

type Reverse<S extends string> =
  S extends `${infer Head}${infer Tail}`
    ? `${Reverse<Tail>}${Head}`
    : ''

type StartsWith<T extends string, U extends string> =
  T extends `${U}${string}` ? true : false

type EndsWith<T extends string, U extends string> =
  StartsWith<Reverse<T>, Reverse<U>>

This is a fun composition but far less efficient than Approach 1.

Comparison with StartsWith (2688)

StartsWith and EndsWith are mirror images of each other:

// StartsWith: prefix first
type StartsWith<T extends string, U extends string> =
  T extends `${U}${string}` ? true : false

// EndsWith: suffix last
type EndsWith<T extends string, U extends string> =
  T extends `${string}${U}` ? true : false

The only difference is the position of ${string} in the template literal pattern.

Key Takeaways