type-challenges、re-challenge

type-challenges に挫折した者が再チャレンジする記録

Hello World
level: warm-up
/* _____________ Your Code Here _____________ */
type HelloWorld = string // expected to be a string
/* _____________ Test Cases _____________ */
import type { Equal, Expect, NotAny } from '@type-challenges/utils'
type cases = [
Expect<NotAny<HelloWorld>>,
Expect<Equal<HelloWorld, string>>,
]

Pick
level: easy
アプローチ
とりあえず、
type MyPick<T, K> = {'title': string}
と書いて1つ目を通してみた
その後
type MyPick<T, K> = {[P in K]: T[P]}
と書いて、うまくいったように見えるが型定義自体にエラーが残っている
- Type 'K' is not assignable to type 'string | number | symbol'.(2322)
- Type 'P' cannot be used to index type 'T'.(2536)
in と keyof を組み合わせて使う場合が存在するらしいが、使い分けがよくわからなかったので調べた
- in の使いどころ(JSの役割(プロパティの存在真偽、ループのためのin)は省略)
mapped typesで使う
構文は、[P in K]([]がない状態で使われることはおそらくない)
インデックスシグネチャ(宣言済みオブジェクトに新たなプロパティを追加することができる)の概念が前提
[P in K] と書けば、KからPを取り出す - keyof の使いどころ
プロパティ名を取得する
例
type X = 'apple' | 'banana'
type NewObj = {[P in X]: string}
// type NewObj = {
// apple: string;
// banana: string;
// }
type keys = keyof NewObj
// 'apple' | 'banana'
type Obj2 =
{
[P in keyof NewObj]: NewObj[P]
}
// type Obj2 = {
// apple: string;
// banana: string;
// }
色々調べてこう書いたが、
type MyPick<T, K> = {[P in keyof T]: T[P]}
別のエラーが残っている
- Type 'false' does not satisfy the constraint 'true'.
- Type 'false' does not satisfy the constraint 'true'.
keyof は T の全プロパティを取得するものだけど、全部必要ないことに気づいたので
type MyPick<T, K> = {[P in K]: T[P]}
とした
Kに制約がない(KにTの要素じゃないものが入ってしまう)ことに気づいた
type MyPick<T, [K in T]> = {[P in K]: T[P]}
としてみたけど、文法が正しくない
type MyPick<T, K extends T> = {[P in K]: T[P]}
extends T だと、{}になってしまい、string を assign できない
type MyPick<T, K extends keyof T> = {[P in K]: T[P]}
とすることで解決
解答
/* _____________ Your Code Here _____________ */
type MyPick<T, K extends keyof T> = {[P in K]: T[P]}
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Expected1, MyPick<Todo, 'title'>>>,
Expect<Equal<Expected2, MyPick<Todo, 'title' | 'completed'>>>,
// @ts-expect-error
MyPick<Todo, 'title' | 'completed' | 'invalid'>,
]
interface Todo {
title: string
description: string
completed: boolean
}
interface Expected1 {
title: string
}
interface Expected2 {
title: string
completed: boolean
}

Readonly
level: easy
アプローチ
とりあえず、以下のようにしてエラーを回避
type MyReadonly<T> = {
readonly title: string
readonly description: string
readonly completed: boolean
readonly meta: {
author: string
}
}
ただ、これでは汎用性がないので、
type MyReadonly<T> = {readonly [K in keyof T]: T[K]}
こんな感じ?
エラーは消えたけど、authorがreadonlyになっているのか自信がない
const x: MyReadonly<Todo1> = {
title: 'a',
description: 'b',
completed: true,
meta: {
author: 'c'
}
}
x.meta.author = 'd'
代入できてしまった、、、
本家Readonlyもプロパティ直下のみreadonlyとしているので問題なさそう
readonly は、別のオブジェクトを介すことで書き換えができてしまうようなので注意が必要なよう
解答
/* _____________ Your Code Here _____________ */
type MyReadonly<T> = {readonly [K in keyof T]: T[K]}
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<MyReadonly<Todo1>, Readonly<Todo1>>>,
]
interface Todo1 {
title: string
description: string
completed: boolean
meta: {
author: string
}
}

Tuple to Object[WIP]
level: easy
アプローチ
インデックスアクセス型というものがあり、T[number]と書くことで配列の要素の型を参照できる
type TupleToObject<T extends readonly any[]> = {[typeof T[number]]: T}
と書いてみた
Tは配列型なのでオブジェクトのプロパティの型として使えない
type TupleToObject<T extends readonly any[]> = {T: T[number]}
T: と書くと'T'というプロパティとなってしまう
in keyof を使ってみる
type TupleToObject<T extends readonly any[]> = {[P in keyof T]: T[number]}
しかし、これは配列型なので、得られるのはindex(0, 1, 2, ...)である
そこでT[P]を使えば良いのではと思い
type TupleToObject<T extends readonly any[]> = {T[P in keyof T]: T[number]}
しかし、これは、T['tesla'] というようになってしまうためうまくいかない
T[number]とすれば良いことを思い出し、keyに書いてみるが
type TupleToObject<T extends readonly any[]> = {[P in T[number]]: [P in T[number]]}
も
type TupleToObject<T extends readonly any[]> = {[P in T[number]]: T[P]}
うまくいかなかった
Pが前半で列挙されているため、そのまま利用できる
type TupleToObject<T extends readonly any[]> = {[P in T[number]]: P}
として正解
2024/11/28 追記
上記のコードはtype error = TupleToObject<[[1, 2], {}]>
が通るコードだったため修正が必要
any[] により、混合したタプルも受け入れてしまうようになっている
(string | number | symbol)[]
に変更することで解決
解答
/* _____________ Your Code Here _____________ */
type TupleToObject<T extends readonly (string | number | symbol)[]> = {[P in T[number]]: P}
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
const tupleNumber = [1, 2, 3, 4] as const
const sym1 = Symbol(1)
const sym2 = Symbol(2)
const tupleSymbol = [sym1, sym2] as const
const tupleMix = [1, '2', 3, '4', sym1] as const
type cases = [
Expect<Equal<TupleToObject<typeof tuple>, { 'tesla': 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y' }>>,
Expect<Equal<TupleToObject<typeof tupleNumber>, { 1: 1, 2: 2, 3: 3, 4: 4 }>>,
Expect<Equal<TupleToObject<typeof tupleSymbol>, { [sym1]: typeof sym1, [sym2]: typeof sym2 }>>,
Expect<Equal<TupleToObject<typeof tupleMix>, { 1: 1, '2': '2', 3: 3, '4': '4', [sym1]: typeof sym1 }>>,
]
// @ts-expect-error
type error = TupleToObject<[[1, 2], {}]>

First of Array
level: easy
アプローチ
type First<T extends any[]> = T[0]
とりあえず0番目を取得
このままだと、Expect<Equal<First<[]>, never>>
が通らない
条件分岐ができるConditional Typesを検討してみる
type First<T extends any[]> = T[0] extends any[] ? never : T[0]
まだExpect<Equal<First<[]>, never>>
だけ通らない
逆だとこれだけ通るので、First<[]> が any[]の部分型でないことはわかる
そもそも空配列なら、T[0]を持たないのでは
type First<T extends any[]> = T extends [] ? never : T[0]
と書いて正解
解答
/* _____________ Your Code Here _____________ */
type First<T extends any[]> = T extends [] ? never : T[0]
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<First<[3, 2, 1]>, 3>>,
Expect<Equal<First<[() => 123, { a: string }]>, () => 123>>,
Expect<Equal<First<[]>, never>>,
Expect<Equal<First<[undefined]>, undefined>>,
]
type errors = [
// @ts-expect-error
First<'notArray'>,
// @ts-expect-error
First<{ 0: 'arrayLike' }>,
]

Length of Tuple
level: easy
アプローチ
type Length<T> = T['length']
型変数のlengthプロパティへアクセスすることができる
エラーは消えたが、型定義自体にエラーが発生した
- Type '"length"' cannot be used to index type 'T'.
これはおそらく、Tがlengthを持つかわからないため
type Length<T extends (string | number | symbol)[]> = T['length']
と書くと
- Type 'readonly ["tesla", "model 3", "model X", "model Y"]' does not satisfy the constraint '(string | number | symbol)[]'.
The type 'readonly ["tesla", "model 3", "model X", "model Y"]' is 'readonly' and cannot be assigned to the mutable type '(string | number | symbol)[]'. - Type 'readonly ["FALCON 9", "FALCON HEAVY", "DRAGON", "STARSHIP", "HUMAN SPACEFLIGHT"]' does not satisfy the constraint '(string | number | symbol)[]'.
The type 'readonly ["FALCON 9", "FALCON HEAVY", "DRAGON", "STARSHIP", "HUMAN SPACEFLIGHT"]' is 'readonly' and cannot be assigned to the mutable type '(string | number | symbol)[]'.
というようなエラーが出た
これは変数定義でas constをつけているためreadonlyとなり、readonlyな型は変更可能な型への代入ができないためエラーとなっている
type Length<T extends readonly (string | number | symbol)[]> = T['length']
readonly を加えてあげることで解決
(numberとsymbol、特にsymbolは不要かもしれない)
解答
/* _____________ Your Code Here _____________ */
type Length<T extends readonly (string | number | symbol)[]> = T['length']
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
const tesla = ['tesla', 'model 3', 'model X', 'model Y'] as const
const spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT'] as const
type cases = [
Expect<Equal<Length<typeof tesla>, 4>>,
Expect<Equal<Length<typeof spaceX>, 5>>,
// @ts-expect-error
Length<5>,
// @ts-expect-error
Length<'hello world'>,
]

Exclude
level: easy
アプローチ
extendsを使って分岐する
T('a' | 'b' | 'c')がU('a')に割り当て可能かどうかで判断し、
割り当てられるなら(取り除かれるため)返す型に含めず、割り当てられないなら(残すため)返す型に含める処理を書く
T extends U ? {返さない} : T
{}のところがわからなかったが、never
を使えば良いらしい
never
型は返す値がないときに使う型
null
型やvoid
型と違うところは、
null
型やvoid
型は型として残ってしまうところ
例えば'a' | never
とした際、残るのは'a'
だが、'a' | null
は'a' | null
、'a' | void
は'a' | void
と残ってしまう
never
型は情報数学(オートマトン)のεのような役割だなと思った
解答
/* _____________ Your Code Here _____________ */
type MyExclude<T, U> = T extends U ? never : T
/* _____________ Test Cases _____________ */
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>>,
]

Awaited
level: easy
アプローチ
Promise
型の調査から
Promise
はJSにおけるコールバック地獄を解消するために生まれたもの
値が返ってくるかエラーが返ってくるかを保証する
Promise
型は型引数に指定した型を返す(指定しない場合はanyを返す)
Promise
でいうところのFullfilledにあたる
今回再帰的な型の取得が必要となる
infer
を使えば良さそう
infer
は推論を意味し、conditonal typeで条件分岐した型を表現できる
infer R
と書くとinfer
で推論して型変数Rに代入できる
ひとまずこんな感じに書いた
type MyAwaited<T> = T extends Promise<infer R> ? R : never
これで最初の2つのケースには対応できたが、再帰的なPromiseには対応できていない
根本的な解決になっていないかもしれないが2重に対応
type MyAwaited<T> = T extends Promise<infer R> ? (R extends Promise<infer S> ? S : R) : never
3重の場合
type MyAwaited<T> = T extends Promise<infer R> ? (R extends Promise<infer S> ? (S extends Promise<infer U> ? U : S): R) : never
type MyAwaited<T> = T extends Promise<infer R> ? (R extends Promise<infer S> ? (S extends Promise<infer U> ? U : S): R) : number
これでエラーは直るが、嘘解法
type MyAwaited<T> = T extends Promise<infer R> ? R : MyAwaited<R>
上記を書く前にこれを試したけど、以下のエラーが出てしまっていた
- Type instantiation is excessively deep and possibly infinite.
- Cannot find name 'R'.
これは無限ループを起こしてしまう可能性があることと、型変数はfalse時に使えないことを意味している
type MyAwaited<T> = T extends Promise<infer R> ? MyAwaited<R> : T
と書くことで最後のケース以外をクリアできた
type MyAwaited<T> = T extends { then: (onfulfilled: (arg: infer X) => any) => any } ? X : (Promise<infer R> ? MyAwaited<R> : T)
と書いて条件分岐を分けてみた
- 'MyAwaited' only refers to a type, but is being used as a value here.
- Cannot find name 'R'.
- 'T' only refers to a type, but is being used as a value here.
エラーが出る
後半の括弧内でextendsが抜けていた
type MyAwaited<T> = T extends { then: (onfulfilled: (arg: infer X) => any) => any } ? X : (T extends Promise<infer R> ? MyAwaited<R> : T)
文法エラーは消えた
type MyAwaited<T> = T extends { then: (onfulfilled: (arg: infer X) => any) => any } ? MyAwaited<X> : (T extends Promise<infer R> ? MyAwaited<R> : T)
再帰していく中で最終的には、MyAwaited<X>のX部分が返したい型となるので
type MyAwaited<T> = T extends { then: (onfulfilled: (arg: infer X) => any) => any } ? MyAwaited<X> : T
と書ける
Promiseの内部は
{ then: (onfulfilled: (arg: infer X) => any) => any }
の部分型となるため通る
他の答えを探していたら
PromiseLikeというものがあることを知った
T extends PromiseLike<infer R> ? MyAwaited<R> : T
こんな簡潔に書けるらしい
解答
/* _____________ Your Code Here _____________ */
type MyAwaited<T> = T extends { then: (onfulfilled: (arg: infer X) => any) => any }
? MyAwaited<X>
: T
/* _____________ Test Cases _____________ */
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 T = { then: (onfulfilled: (arg: number) => any) => any }
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>>,
Expect<Equal<MyAwaited<T>, number>>,
]

If
level: easy
アプローチ
第一引数がtruthyな値なら第二引数の形を、第一引数がfalsyな値なら第三引数の型を返す
truthy/falsy な値とは
true/false とみなされる値のこと
例えばnullもfalsyな値
今回C
は true
or false
をとるためあんまり関係ない
type If<C, T, F> = C extends true ? T : F
通常のケースは通ったが、エラーのケースがエラーにならない
Cに型制約を課すことでうまくいく
type If<C extends boolean, T, F> = C extends true ? T : F
3番目のケースのboolean
は true | false
をとるため、'a' | 2
となる
解答
/* _____________ Your Code Here _____________ */
type If<C extends boolean, T, F> = C extends true ? T : F
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<If<true, 'a', 'b'>, 'a'>>,
Expect<Equal<If<false, 'a', 2>, 2>>,
Expect<Equal<If<boolean, 'a', 2>, 'a' | 2>>,
]
// @ts-expect-error
type error = If<null, 'a', 'b'>

Concat
level: easy
アプローチ
一旦intersection型を考えた
&で結んだが、それでは最初のケースしか通らない
& は2つの型の共通をとり、| は2つの型をそれぞれとるから
今回はmergeが必要
JSに使われるspread構文(配列の値を展開するもの)がTypeScriptにもあるらしい
type Concat<T, U> = [...T, ...U]
と書くことでうまくいったように見えるが、エラーのケースがエラーになっていないのと、
- A rest element type must be an array type.
というエラーがある
Tが配列型であることを明示する必要がある
type Concat<T extends [], U extends []> = [...T, ...U]
extends を用いた定義によって、型定義のエラーとエラーケースの問題は解消できた
しかしextends []と書いたことによって、最初のケース以外がエラーとなってしまった
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]
とすることで、空の場合と空でない場合の両者に対応できる
ちなみにT extends Array<T>だと値が入っていることが前提となる
エラーが1つ残っている
- Type 'readonly [1]' does not satisfy the constraint 'unknown[]'.
The type 'readonly [1]' is 'readonly' and cannot be assigned to the mutable type 'unknown[]'.
これは as const をつけることによってreadonlyとなっているためである
type Concat<T extends readonly unknown[], U extends readonly unknown[]> = [...T, ...U]
readonly をつけることによってエラーを解消した
解答
/* _____________ Your Code Here _____________ */
type Concat<T extends readonly unknown[], U extends readonly unknown[]> = [...T, ...U]
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
const tuple = [1] as const
type cases = [
Expect<Equal<Concat<[], []>, []>>,
Expect<Equal<Concat<[], [1]>, [1]>>,
Expect<Equal<Concat<typeof tuple, typeof tuple>, [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']>>,
]
// @ts-expect-error
type error = Concat<null, undefined>

Includes
level: easy
アプローチ
type Includes<T extends readonly any[], U> = U extends T ? true : false
と書いてみた
一部エラーが残る
trueの場合がうまくいっていない
確かに最初のケースだと'Kars'が['Kars', 'Esidisi', 'Wamuu', 'Santana']の部分型とは言えないためfalseとなってしまう
type Includes<T extends readonly any[], U> = U extends typeof T ? true : false
- 'T' only refers to a type, but is being used as a value here.
おかしな型定義をしていた
Tはそもそも型なのでtypeof Tをしても意味がない
type Includes<T extends readonly any[], U> = U extends T[number] ? true : false
厳密な比較ができていないためエラーが残る
例えば、falseはbooleanの部分型であるためtrueを返すが、false自体はincludeされていないためfalseを期待しているためエラーとなる
逆方向もやってみることにした
type Includes<T extends readonly any[], U> = U extends T[number] ? (T[number] extends U ? true : false) : false
エラーが増えた
これは
例えば (1 | 2 | 3) extends 2 は false となってしまうため
要素を1つずつ再帰的に確認してみる
type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Rest] ? (First extends U ? true : Includes<Rest, U>) : false
良さそうに見えるが、エラーが残っていた
例えばIncludes<[1], 1 | 2>の際、1が1|2の部分型かどうかを比較するためtrueになってしまうが、2も左の配列に含まれていなければならないため、falseが期待されるべき
双方向チェックを導入してみる
type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Rest] ? (First extends U ? (U extends First ? true: Includes<Rest, U>) : Includes<Rest, U>) : false
Union Distributionがある
type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Rest] ? ([First] extends [U] ? ([U] extends [First] ? true: Includes<Rest, U>) : Includes<Rest, U>) : false
[]を加えることで分配されることを防ぐ
これによって
Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,
以外のケースは通る
他の回答を調べてみても、問題が改訂される前かEqualを使う方法ばかり
どうやらこのEqualないしEqualの実装を使うしかなさそう
Equal の型比較について extends よりも厳密なものとなっている
Equal の実装は https://github.com/type-challenges/type-challenges/blob/main/utils/index.d.ts にある
export type Equal<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false
extends が3つある式ということと、型引数TをとりTを返す関数がある
とりあえず1と2のところはX, Yを比較するためのものだから、被らないければ他の値でも問題なさそう
問題は<T>() => Tが何をしているか
理解力が足りないのでWIPということで
type Includes<T extends any[], U> = T extends [infer First, ...infer Rest] ? Equal<First, U> extends true ? true : Includes<Rest, U> : false
解答
/* _____________ Your Code Here _____________ */
// type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Rest]
// ? ([First] extends [U] ? ([U] extends [First] ? true: Includes<Rest, U>) : Includes<Rest, U>)
// : false
type Includes<T extends any[], U> = T extends [infer First, ...infer Rest]
? Equal<First, U> extends true ? true : Includes<Rest, U>
: false
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
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>>,
]

Push
level: easy
アプローチ
type Push<T, U> = [...T, ...U]
と書いた
第二引数は配列じゃないので
type Push<T extends [], U> = [...T, U]
と書き、extendsを加えた
[1, 2] などは[]の部分型ではないので
any[]を加えて終了
type Push<T extends any[], U> = [...T, U]
解答
/* _____________ Your Code Here _____________ */
type Push<T extends any[], U> = [...T, U]
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Push<[], 1>, [1]>>,
Expect<Equal<Push<[1, 2], '3'>, [1, 2, '3']>>,
Expect<Equal<Push<['1', 2, '3'], boolean>, ['1', 2, '3', boolean]>>,
]

Unshift
level: easy
アプローチ
特になし
unshift とは、指定要素を配列の先頭に追加するもの
追加時は配列の長さを返すらしい
ref: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
解答
/* _____________ Your Code Here _____________ */
type Unshift<T extends any[], U> = [U, ...T]
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Unshift<[], 1>, [1]>>,
Expect<Equal<Unshift<[1, 2], 0>, [0, 1, 2]>>,
Expect<Equal<Unshift<['1', 2, '3'], boolean>, [boolean, '1', 2, '3']>>,
]

Parameters
level: easy
アプローチ
関数型の引数の型を取得したい
infer
を使えば良さそう
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer R) => any ? R : never
[]にならないかと思ったが、any[]をinfer Rしているので[]の形で返る
解答
/* _____________ Your Code Here _____________ */
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer R) => any ? R : never
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
function foo(arg1: string, arg2: number): void {}
function bar(arg1: boolean, arg2: { a: 'A' }): void {}
function baz(): void {}
type cases = [
Expect<Equal<MyParameters<typeof foo>, [string, number]>>,
Expect<Equal<MyParameters<typeof bar>, [boolean, { a: 'A' }]>>,
Expect<Equal<MyParameters<typeof baz>, []>>,
]

Get Return Type
level: medium
アプローチ
とりあえず
type MyReturnType<T> = T extends () => infer R ? R : never
こんな感じ
- Expect<Equal<1 | 2, MyReturnType<typeof fn>>>,
- Expect<Equal<1 | 2, MyReturnType<typeof fn1>>>,
この2つが通らない
type MyReturnType<T> = T extends (arg: any) => infer R ? R : never
引数を任意で持たせるようにしたら、上の方は通った
複数引数にも対応させる必要がある
type MyReturnType<T> = T extends (arg1: any, arg2: any) => infer R ? R : never
これは嘘解法
type MyReturnType<T> = T extends (...args: never[]) => infer R ? R : never
これで良さそう
この記事が参考になった
unknown型とnever型は対をなす型
unknown型はanyと似た雰囲気を持つが、型の絞り込みをしないと値を扱えないのが利点
解答
/* _____________ Your Code Here _____________ */
type MyReturnType<T> = T extends (...args: never[]) => infer R ? R : never
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<string, MyReturnType<() => string>>>,
Expect<Equal<123, MyReturnType<() => 123>>>,
Expect<Equal<ComplexObject, MyReturnType<() => ComplexObject>>>,
Expect<Equal<Promise<boolean>, MyReturnType<() => Promise<boolean>>>>,
Expect<Equal<() => 'foo', MyReturnType<() => () => 'foo'>>>,
Expect<Equal<1 | 2, MyReturnType<typeof fn>>>,
Expect<Equal<1 | 2, MyReturnType<typeof fn1>>>,
]
type ComplexObject = {
a: [12, 'foo']
bar: 'hello'
prev(): number
}
const fn = (v: boolean) => v ? 1 : 2
const fn1 = (v: boolean, w: any) => v ? 1 : 2