# Notes from Typescript type-challenges

Based on type-challenges here (opens new window)

# Implement Pick

// Create your own Pick implementation

// Goal
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = MyPick<Todo, 'title' | 'completed'>;

const todo: TodoPreview = {
  title: 'Clean room',
  completed: false,
};

// Answer

type MyPick<T, K extends keyof T> = { [Prop in K]: T[Prop] };

Notes:

  • K extends keyof T ensures that K passed in coincides with object keys in T
  • Prop in K:
    • in keyword iterates through the union types in K. Important to note that the in keyword is only able to iterate though union types
    • Prop is simply a placeholder variable representing each element in the union types K. Prop can be replaced with any names.
  • T[Prop] simply returns the type of the corresponding value in Object T, e.g. the type of Todo.title.

# Tuple to Object

// Convert a tuple type to an object type

// Goal
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const;

const result: TupleToObject<typeof tuple> = {
  tesla: 'tesla',
  'model 3': 'model 3',
  'model X': 'model X',
  'model Y': 'model Y',
};

// Answer
type TupleToObject<T extends readonly string[]> = { [Prop in T[number]]: Prop };

Notes:

const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const;

typeof tuple;
// Equals the exact array ['tesla', 'model 3', 'model X', 'model Y']

type TeslaModel = typeof tuple[number];
// Equals the possible values, i.e. the union types 'tesla' | 'model 3' | 'model X' | 'model Y'
  • T extends readonly string[] because typeof tuple equals an array with exacting elements.
  • Prop in T[number] works because T[number] produces union types, allowing in to iterate through it.

# Implement Exclude

// Create your own Exclude implementation

// Goal
type A = 'a' | 'b' | 'c';
type B = 'a';

type MyExclude<A, B> = 'b' | 'c';

// Answer
type MyExclude<T, U> = T extends U ? never : T;

# Access a Wrapped Type

// Goal

type X = Promise<string>
type Y = Promise<{ field: number }>

Awaited<X>
// Awaited utility should return `string` type

Awaited<Y>
// Awaited utility should return `{field: number}` type

// Answer
type Awaited<T extends Promise<any>> = T extends Promise<infer U> ? U : never;

Notes:

  • There is no way to directly access a nested type. The approach here makes use of infer keyword to see if the nested type can be figured out, and uses that.

# Concat Types

// Write a concat type utility that produces the following result
type Result = Concat<[1], [2]>; // expected to be [1, 2]

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