Challenge

Implement StartsWith<T, U> which takes two exact string types and returns whether T starts with U. For example:

type a = StartsWith<"abc", "ac">; // expected to be false
type b = StartsWith<"abc", "ab">; // expected to be true
type c = StartsWith<"abc", "abcd">; // expected to be false

Solution

Knowing about template literal types in TypeScript, the solution becomes really obvious. Let’s start with an initial type that holds any type:

type StartsWith<T, U> = any;

We need to check if the input type parameter T starts with a string literal from U. I’ll do it simpler and just check if the T is U by using conditional types:

type StartsWith<T, U> = T extends `${U}` ? never : never;

If the input type parameter T is the same as in type parameter U, we will go into the true branch of the conditional type. But, we don’t need them to be equal. We need to check if it starts with U. In other words; we don’t care if there will be something after the U in our literal type. So that, use any type there:

type StartsWith<T, U> = T extends `${U}${any}` ? never : never;

If type T matches the pattern of a string that starts with U, we return the true literal type. Otherwise, return false:

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

We got all the test cases passed, but we still got a compilation error saying “Type ‘U’ is not assignable to type ‘string | number | bigint | boolean | null | undefined’.“. That’s because we didn’t add a constraint over generic to show that U is a string. Let’s add it:

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

References