Learn TypeScript w/ Mike North

Challenge 3: Type Challenges

March 22, 2022

Table of Contents

Type challenges are a great way to practice using the TS type system. These are often quite challenging, but they give you valuable practice thinking about how utility types work.

Let’s tackle a curated selection of these challenges ourselves!

Round 1

If<C, T, F>

Implement a type that evaluates to T if the type C is true or F if C is false.

ts
// Implement this type
type If<C, T, F> = never
 
// Tests
type cases = [
Expect<Equal<If<true, "apple", "pear">, "apple">>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<If<false, "orange", 42>, 42>>
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
]
Try

LengthOfTuple<T>

Implement a type that evaluates to a numeric type literal, equivalent to the length of a specified tuple type T

ts
// Implement this type
type LengthOfTuple<T> = never
 
// Tests
const Fruits = ["cherry", "banana"] as const
type cases = [
Expect<Equal<LengthOfTuple<[1, 2, 3]>, 3>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<NotEqual<LengthOfTuple<[1, 2, 3]>, 2>>,
Expect<Equal<LengthOfTuple<typeof Fruits>, 2>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<LengthOfTuple<[]>, 0>>
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
]
Try

EndsWith<A, B>

Implement a type that evaluates to true if the type A ends with the type B, otherwise false.

ts
// Implement this type
type EndsWith<A, B> = any
 
// Tests
type cases = [
Expect<Equal<EndsWith<"ice cream", "cream">, true>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<EndsWith<"ice cream", "chocolate">, false>>
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
]
Try
Click for hints
ts
type FirstLetter<S> = S extends `${infer F}${string}`
? F
: never
let firstLetterOfBird: FirstLetter<"bird">
let firstLetterOfBird: "b"
let firstLetterOfDog: FirstLetter<"dog">
let firstLetterOfDog: "d"
Try

Concat<A, B>

Implement a type that concatenates two tuple types A, and B

ts
// Implement this type
type Concat<A, B> = any
 
// Tests
type cases = [
Expect<Equal<Concat<[], []>, []>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<Concat<[], ["hello"]>, ["hello"]>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<
Equal<Concat<[18, 19], [20, 21]>, [18, 19, 20, 21]>
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
>,
Expect<
Equal<
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Concat<[42, "a", "b"], [Promise<boolean>]>,
[42, "a", "b", Promise<boolean>]
>
>
]
Try

Round 2

ReturnOf<F>

Implement a type that emits the return type of a function type F

ts
// Implement this type
type ReturnOf<F> = never
 
// Tests
 
const flipCoin = () =>
Math.random() > 0.5 ? "heads" : "tails"
const rockPaperScissors = (arg: 1 | 2 | 3) => {
return arg === 1
? ("rock" as const)
: arg === 2
? ("paper" as const)
: ("scissors" as const)
}
 
type cases = [
// simple 1
Expect<Equal<boolean, ReturnOf<() => boolean>>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
// simple 2
Expect<Equal<123, ReturnOf<() => 123>>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<
Equal<ComplexObject, ReturnOf<() => ComplexObject>>
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
>,
Expect<
Equal<
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Promise<boolean>,
ReturnOf<() => Promise<boolean>>
>
>,
Expect<Equal<() => "foo", ReturnOf<() => () => "foo">>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<
Equal<"heads" | "tails", ReturnOf<typeof flipCoin>>
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
>,
Expect<
Equal<
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
"rock" | "paper" | "scissors",
ReturnOf<typeof rockPaperScissors>
>
>
]
 
type ComplexObject = {
a: [12, "foo"]
bar: "hello"
prev(): number
}
Try

Split<S, SEP>

Implement a type that splits a string literal type S by a delimiter SEP, emitting a tuple type containing the string literal types for all of the “tokens”

ts
// Implement this type
type Split<S extends string, SEP extends string> = any
 
// Tests
 
type cases = [
Expect<
Equal<
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Split<"Hi! How are you?", "z">,
["Hi! How are you?"]
>
>,
Expect<
Equal<
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Split<"Hi! How are you?", " ">,
["Hi!", "How", "are", "you?"]
>
>,
Expect<
Equal<
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Split<"Hi! How are you?", "">,
[
"H",
"i",
"!",
" ",
"H",
"o",
"w",
" ",
"a",
"r",
"e",
" ",
"y",
"o",
"u",
"?"
]
>
>,
Expect<Equal<Split<"", "">, []>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<Split<"", "z">, [""]>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<Split<string, "whatever">, string[]>>
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
]
Try

IsTuple<T>

Implement a type IsTuple, which takes an input type T and returns whether T is tuple type.

ts
// Implement this type
type IsTuple<T> = any
 
// Tests
type cases = [
Expect<Equal<IsTuple<[]>, true>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<IsTuple<[number]>, true>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<IsTuple<readonly [1]>, true>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<IsTuple<{ length: 1 }>, false>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<IsTuple<number[]>, false>>
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
]
Try
Click for hints
ts
type TypeExtends<A, B> = A extends B ? true : false
let t0: TypeExtends<number, 6>
let t1: TypeExtends<6, number>
Try

Round 3

TupleToNestedObject<P, V>

Given a tuple type T that only contains string type, and a type U, build an object recursively.

ts
// Implement this type
type TupleToNestedObject<P, V> = any
 
// Tests
 
type cases = [
Expect<
Equal<TupleToNestedObject<["a"], string>, { a: string }>
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
>,
Expect<
Equal<
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
TupleToNestedObject<["a", "b"], number>,
{ a: { b: number } }
>
>,
Expect<
Equal<
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
TupleToNestedObject<["a", "b", "c"], boolean>,
{ a: { b: { c: boolean } } }
>
>,
Expect<Equal<TupleToNestedObject<[], boolean>, boolean>>
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
]
Try
Click for hints
ts
type Pets = "dog"
type PetsObj = {
[K in Pets]: Promise<number>
}
let x: PetsObj
type PetsObj = { dog: Promise<number>; }
Try

IndexOf<T, U>

Implement the type version of Array.indexOf, IndexOf<T, U> takes an Array T, any U and returns the index of the first U in Array T.

ts
// Implement this type
type IndexOf<T, U> = any
 
// Tests
 
type cases = [
Expect<Equal<IndexOf<[1, 2, 3], 2>, 1>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<IndexOf<[2, 6, 3, 8, 4, 1, 7, 3, 9], 3>, 2>>,
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
Expect<Equal<IndexOf<[0, 0, 0], 2>, -1>>
Type 'false' does not satisfy the constraint 'true'.2344Type 'false' does not satisfy the constraint 'true'.
]
Try


© 2023 All Rights Reserved