# Type challenges
# 雑多メモ
- 左辺の
extends
は、受け取れる型の定義に使う - 右辺の
extends
は、型の検査(===や!==に近いイメージ)に使う- ここでUnion型を使った場合は、総当りで処理されるイメージになる(詳細は
Exclude
を参照)
- ここでUnion型を使った場合は、総当りで処理されるイメージになる(詳細は
- オブジェクトのkey名部分では
[P in ***]
のような表記ができる。このP
は右辺でそのまま使える。 SomeArr[number]
で、配列型をUnion型に変換できるtype List = (string | number | boolean)[] // - string | number | boolean になる // - 配列が持ちうる全ての型を、Union型にまとめる感じ type Elem = List[number]
# --- 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
// as const の表記が重要。
// - これがあるから、typeof tuple は ['tesla', 'model 3', 'model X', 'model Y'] という配列型になる
// - これがないと、typeof tuple はただのstring[]になる
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]
}
# Deep Readonly
pass あまりいい実装がない
# Tuple to Union
// 変数ではなく tuple type である点に注意
type Arr = ['1', '2', '3']
const a: TupleToUnion<Arr> // expected to be '1' | '2' | '3'
type TupleToUnion<T extends any[]> = T[number]