Typescript challenge practice series(4)
Aaron Zhang Author

logo

00268 Easy If

https://github.com/type-challenges/type-challenges/tree/main/questions/00268-easy-if

Implement a util If which accepts condition C, a truthy return type T, and a falsy return type F. C is expected to be either true or false while T and F can be any type.

For example:

type A = If<true, "a", "b">; // expected to be 'a'
type B = If<false, "a", "b">; // expected to be 'b'

Tests

import type { Equal, Expect } from "@type-challenges/utils";

type cases = [
  Expect<Equal<If<true, "a", "b">, "a">>,
  Expect<Equal<If<false, "a", 2>, 2>>
];

// @ts-expect-error
type error = If<null, "a", "b">;

Solution

This is an easy one. From previous questions we know we can restrict C to extend boolean and use a tertiary expression to return T or F.

type If<C extends boolean, T, F> = C extends true ? T : F;

By now we should get all tests pass. If thereโ€™s still any error in type error = If<null, 'a', 'b'>, it is because in TS non-strict mode, null can extend boolean. We can simple turn on strict mode.

    "strict": true, // Enable all strict type-checking options.
    "strictNullChecks": true, // When type checking, take into account 'null' and 'undefined'.

00898 Easy includes

https://github.com/type-challenges/type-challenges/tree/main/questions/00898-easy-includes

Implement the JavaScript Array.includes function in the type system. A type takes the two arguments. The output should be a boolean true or false.

For example:

type isPillarMen = Includes<["Kars", "Esidisi", "Wamuu", "Santana"], "Dio">; // expected to be `false`

Test cases

import type { Equal, Expect } from "@type-challenges/utils";
import { Includes } from "./template";

type cases = [
  Expect<
    Equal<Includes<["Kars", "Esidisi", "Wamuu", "Santana"], "Kars">, true>
  >,
  Expect<
    Equal<Includes<["Kars", "Esidisi", "Wamuu", "Santana"], "Dio">, false>
  >,
  Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>,
  Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>,
  Expect<Equal<Includes<[1, 2, 3], 2>, true>>,
  Expect<Equal<Includes<[1, 2, 3], 1>, true>>,
  Expect<Equal<Includes<[{}], { a: "A" }>, false>>,
  Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
  Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
  Expect<Equal<Includes<[false, 2, 3, 5, 6, 7], false>, true>>,
  Expect<Equal<Includes<[{ a: "A" }], { readonly a: "A" }>, false>>,
  Expect<Equal<Includes<[{ readonly a: "A" }], { a: "A" }>, false>>,
  Expect<Equal<Includes<[1], 1 | 2>, false>>,
  Expect<Equal<Includes<[1 | 2], 1>, false>>,
  Expect<Equal<Includes<[null], undefined>, false>>,
  Expect<Equal<Includes<[undefined], null>, false>>
];

Solution

My first thought on this one would be this:

type Includes<T extends readonly any[], U> = U extends T[number] ? true : false;

Unfortunately, it fails on some tests. The reason is the expression U extends T[number] ? true : false will compare the type of every elements in T with U, and return true or false for each comparison, in the end, the type would be a union type of all true/false results.
true | true would be true and true | false will be boolean

We can use another approach to use infer to get each element of the array and use the provided Equal function to recursively do comparisons.

type Includes<T extends readonly any[], U>
  = T extends [infer FIRST, ...infer REST]
    ? Equal<FIRST, U> ? true : Includes<REST, U>
    : false

Summary

We can recursively use this Include type by passing REST as a generic type.

03057 Easy Push & 03060 Easy Unshift

https://github.com/type-challenges/type-challenges/tree/main/questions/03057-easy-push
https://github.com/type-challenges/type-challenges/tree/main/questions/03060-easy-unshift

Implement the generic version of Array.push

For example:

type Result = Push<[1, 2], "3">; // [1, 2, '3']

Implement the type version of Array.unshift

For example:

type Result = Unshift<[1, 2], 0>; // [0, 1, 2,]

The Solution would be the same as concat, which is to use the spread operator to create a new array type.

// Push
type Push<T extends any[], U> = [...T, U];

// Unshift
type Push<T extends any[], U> = [...T, U];

Summary

The spread operator can also be used on type.

03312 Easy Parameter

Implement the built-in Parameters<T> generic without using it.

For example:

const foo = (arg1: string, arg2: number): void => {};

type FunctionParamsType = MyParameters<typeof foo>; // [arg1: string, arg2: number]

Testing

import type { Equal, Expect } from "@type-challenges/utils";

const foo = (arg1: string, arg2: number): void => {};
const bar = (arg1: boolean, arg2: { a: "A" }): void => {};
const baz = (): void => {};

type cases = [
  Expect<Equal<MyParameters<typeof foo>, [string, number]>>,
  Expect<Equal<MyParameters<typeof bar>, [boolean, { a: "A" }]>>,
  Expect<Equal<MyParameters<typeof baz>, []>>
];

Solution

We can use infer to get the type of function parameter.

type MyParameters<T extends (...args: any[]) => any> = T extends (...infer ARGS) => any ? ARGS : never

Till now, we have finish all EASY level questions.๐ŸŽ‰๐ŸŽ‰

 Comments