00043 Easy Exclude
https://github.com/type-challenges/type-challenges/tree/main/questions/00043-easy-exclude
Implement the built-in Exclude<T, U>
type Result = MyExclude<"a" | "b" | "c", "a">;
Tests
import type { Equal, Expect } from "@type-challenges/utils";
type cases = [
  Expect<Equal<MyExclude<"a" | "b" | "c", "a">, "b" | "c">>,
  Expect<Equal<MyExclude<"a" | "b" | "c", "a" | "b">, "c">>,
  Expect<
    Equal<MyExclude<string | number | (() => void), Function>, string | number>
  >
];
Solution
When both sides of extends are union types, extends will iterate through each type inside to check.
type MyExclude<T, U> = T extends U ? never : T;
Summary
👉 T extends U
Union type will iterate through each union type to check if every type in T exists in a type in U. It works like a for loop in javascript. And it will return the result as a union type as well.
00533 Easy Concat
https://github.com/type-challenges/type-challenges/tree/main/questions/00533-easy-concat
Implement the JavaScript Array.concat function in the type system. A type takes the two arguments. The output should be a new array that includes inputs in ltr order.
For example:
type Result = Concat<[1], [2]>; // expected to be [1, 2]
Tests
type cases = [
  Expect<Equal<Concat<[], []>, []>>,
  Expect<Equal<Concat<[], [1]>, [1]>>,
  Expect<Equal<Concat<[1, 2], [3, 4]>, [1, 2, 3, 4]>>,
  Expect<
    Equal<
      Concat<["1", 2, "3"], [false, boolean, "4"]>,
      ["1", 2, "3", false, boolean, "4"]
    >
  >
];
Solution
This one can be easily done by using spread operator:
type Concat<T extends any[], U extends any[]> = [...T, ...U];
Summary
👉 Spread operator ...
Spread operator can also be used in Typescript.
00189 Easy Awaited
We need to get a type which is inside the wrapped type.
type ExampleType = Promise<string>;
type Result = MyAwaited<ExampleType>; // string
Tests
import type { Equal, Expect } from "@type-challenges/utils";
type X = Promise<string>;
type Y = Promise<{ field: number }>;
type Z = Promise<Promise<string | number>>;
type Z1 = Promise<Promise<Promise<string | boolean>>>;
type cases = [
  Expect<Equal<MyAwaited<X>, string>>,
  Expect<Equal<MyAwaited<Y>, { field: number }>>,
  Expect<Equal<MyAwaited<Z>, string | number>>,
  Expect<Equal<MyAwaited<Z1>, string | boolean>>
];
// @ts-expect-error
type error = MyAwaited<number>;
Solution
First of all, we can see MyAwaited only accepts Promise type as its generic type, so we can do this first:
type MyAwaited<T extends Promise<any>> = any;
Then, we can use infer to get the type inside the Promise
type MyAwaited<T extends Promise<any>> = T extends Promise<infer INSIDE>
  ? INSIDE
  : never;
At this stage, we have solved first two of the test cases. Remaining test cases required us to get the type inside the nested Promise type. So we need to check the INSIDE type is a Promise type as well. If so, we can recursively use MyAwaited type.
type MyAwaited<T extends Promise<any>> = T extends Promise<infer INSIDE>
  ? INSIDE extends Promise<any>
    ? MyAwaited<INSIDE>
    : INSIDE
  : never;
By using MyAwaited recursively, we can eventually get the type inside.
Summary
👉 infer can only be used in conditional type.
More info on
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html