# Type challenges

# 雑多メモ

  • 左辺のextendsは、受け取れる型の定義に使う
  • 右辺のextendsは、型の検査(===や!==に近いイメージ)に使う
    • ここでUnion型を使った場合は、総当りで処理されるイメージになる(詳細はExcludeを参照)
  • オブジェクトのkey名部分では[P in ***]のような表記ができる。このPは右辺でそのまま使える。
  • SomeArr[number]で配列型をUnion型に変換できる
    type List = (string | number | boolean)[]
    type Elem = List[number] // string | number | boolean になる
    

# --- Challenges - Easy ---

# Pick

interface Todo {
  title: string
  description: string
  completed: boolean
}

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

const todo: TodoPreview = {
    title: 'Clean room',
    completed: false,
}
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

# Readonly

interface Todo {
  title: string
  description: string
}

const todo: MyReadonly<Todo> = {
  title: "Hey",
  description: "foobar"
}
type MyReadonly<T> = {
  readonly [P in keyof T]: T[P]
}

# Tuple to object

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

// expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
const result: TupleToObject<typeof tuple>;
type TupleToObject<T extends readonly string[]> = {
  [P in T[number]]: P;
};

# First of Array

type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]

type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
type First<T extends any[]> = T extends [] ? never : T[0];

# Length of Tuple

type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']

type teslaLength = Length<tesla>  // expected 4
type spaceXLength = Length<spaceX> // expected 5
type Length<T extends any[]> = T['length']

# Exclude

// expected string | Function
type Sample = MyExclude<string | number | boolean | Function, number | boolean>;
type MyExclude<T, U> = T extends U ? never : T;

# Awaited

type MyPromise = Promise<number>
// expected number
type MyPromiseResult = Awaited<MyPromise>
type Awaited<T extends Promise<any>> = T extends Promise<infer A> ? A : never;

# If

type A = If<true, 'a', 'b'>; // expected to be 'a'
type B = If<false, 'a', 'b'>; // expected to be 'b'
type If<T, U, V> = T extends true ? U : V;

# Concat

type Result = Concat<[1], [2]>; // expected to be [1, 2]
type Concat<T extends any[], U extends any[]> = [...T, ...U];

# Includes

// expected to be `true`
type IsIncluded = Includes<['BMW', 'Mercedes', 'Audi'], 'BMW'>;
type Includes<T extends any[], U> = U extends T[number] ? true : false;

# Push

type Result = Push<[1, 2], 3> // [1, 2, 3]
type Push<T extends any[], U> = [...T, U]

# Unshift

type Result = Unshift<[1, 2], 0>; // [0, 1, 2]
type Unshift<T extends any[], U> = [U, ...T];

# Parameters

type MyFunction = (i: number, s:string) => void;
// expects [i: number, s: string]
type MyParameter = Parameter<MyFunction>
type Parameter<T> = T extends (...args: infer A) => any ? A : never;

# --- Challenges - Medium ---

# Get Return Type

const fn = (v: boolean) => {
  if (v) return 1;
  else return 2;
};

type a = MyReturnType<typeof fn>; // should be "1 | 2"
type MyReturnType<T> = T extends (...args) => infer R ? R : never;

# Omit

interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPreview = MyOmit<Todo, 'description' | 'title'>

const todo: TodoPreview = {
  completed: false,
}
type MyOmit<T, U> = {
  [P in Exclude<keyof T, U>]: T[P]
}

# Readonly 2

interface Todo {
  title: string
  description: string
  completed: boolean
}

const todo: MyReadonly2<Todo, 'title' | 'description'> = {
  title: "Hey",
  description: "foobar",
  completed: false,
}

todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
todo.completed = true // OK
type MyReadonly2<T, K extends keyof T> = T & {
  readonly [P in K]: T[P]
}