Closed15

type-challengesを実践する

LeivyLeivy

まずはクローンする

git clone git@github.com:type-challenges/type-challenges.git

インストールする

pnpm install

pnpm generate

pnpmのバージョンが合わずに以下のエラーが出る場合がある

 ERR_PNPM_BAD_PM_VERSION  This project is configured to use v8.12.1 of pnpm. Your current pnpm is v9.0.5

以下のコマンドでエラーを回避する

pnpm config set package-manager-strict false

再度pnpm generateを実行し、言語を選択する

Generating local playground...

✔ Select language: › ja

Local playground generated at:

playgroundディレクトリに選択した言語のチャレンジファイルが生成される

LeivyLeivy

00004-easy-pick

    /*
      4 - Pick
      -------
      by Anthony Fu (@antfu) #初級 #union #built-in
    
      ### 質問
    
      組み込みの型ユーティリティ`Pick<T, K>`を使用せず、`T`から`K`のプロパティを抽出する型を実装します。
    
      例えば:
    
      ```ts
      interface Todo {
        title: string
        description: string
        completed: boolean
      }
    
      type TodoPreview = MyPick<Todo, 'title' | 'completed'>
    
      const todo: TodoPreview = {
          title: 'Clean room',
          completed: false,
      }
      ```
    
      > GitHubで確認する:https://tsch.js.org/4/ja
    */
    
    /* _____________ ここにコードを記入 _____________ */
    
    type MyPick<T, K extends keyof T> = {
      [key in K]: T[key];
    };
    
    /* _____________ テストケース _____________ */
    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
    }    
  

keyof 演算子

オブジェクト型からそのプロパティ名を型として取得します。
例: keyof Todo 'title' | 'description' | 'completed' という型になります。

インデックス型アクセス:

オブジェクト型から特定のプロパティの型を取得します。
例: Todo['title']string 型になります。

マップ型の基本:

新しいオブジェクト型を作る方法です。
[K in ...] の部分で、どのキーを使うかを指定します。
例: { [K in 'a' | 'b']: string } { a: string; b: string } という型になります。


これらを組み合わせて MyPickを作ります:

typescriptCopytype MyPick<T, K extends keyof T> = {
  [key in K]: T[key];
};
  • T は元のオブジェクト型(例: Todo)
  • K は選びたいプロパティ名の型(例: 'title' | 'completed')
  • K extends keyof T で、K が必ず T のプロパティ名であることを保証
  • [key in K] で、選んだプロパティ名それぞれに対して処理
  • T[key] で、元の型からプロパティの型を取得
LeivyLeivy

00007-easy-readonly

    /*
      7 - Readonly
      -------
      by Anthony Fu (@antfu) #初級 #built-in #readonly #object-keys
    
      ### 質問
    
      組み込みの型ユーティリティ`Readonly<T>`を使用せず、`T` のすべてのプロパティを読み取り専用にする型を実装します。実装された型のプロパティは再割り当てできません。
    
      例えば:
    
      ```ts
      interface Todo {
        title: string
        description: string
      }
    
      const todo: MyReadonly<Todo> = {
        title: "Hey",
        description: "foobar"
      }
    
      todo.title = "Hello" // Error: cannot reassign a readonly property
      todo.description = "barFoo" // Error: cannot reassign a readonly property
      ```
    
      > GitHubで確認する:https://tsch.js.org/7/ja
    */
    
    /* _____________ ここにコードを記入 _____________ */
    
    type MyReadonly<T> = {
      readonly [K in keyof T]: T[K]
    }
    
    /* _____________ テストケース _____________ */
    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
      }
    }
    
    /* _____________ 次のステップ _____________ */
    /*
      > 解答を共有する:https://tsch.js.org/7/answer/ja
      > 解答を見る:https://tsch.js.org/7/solutions
      > その他の課題:https://tsch.js.org/ja
    */

type MyReadonly<T> = ...

役割: ジェネリック型 MyReadonly を定義します。
Tは型パラメータで、この型を使用する際に具体的な型が渡されます。

{ ... }

役割: 新しいオブジェクト型を作成することを示します。

readonly

役割: 各プロパティを読み取り専用にするための修飾子です。
これにより、生成される新しい型の全てのプロパティが変更不可能になります。

[K in keyof T]

役割: Mapped Types の構文で、T の全てのプロパティに対してイテレーションを行います。
keyof T: 型 T の全てのプロパティ名を Union 型として取得します。
K in ...: 取得した Union 型の各要素に対して処理を行うことを示します。
K は各イテレーションでの現在のプロパティ名を表す型変数です。

: T[K]

役割: 元の型 T から、現在のプロパティ K に対応する型を取得します。
これにより、新しい型で元の型の構造(各プロパティの型)を維持します。

LeivyLeivy

00011-easy-tuple-to-object

    /*
      11 - Tuple to Object
      -------
      by sinoon (@sinoon) #初級 #object-keys
    
      ### 質問
    
      タプルを受け取り、その各値のkey/valueを持つオブジェクトの型に変換する型を実装します。
    
      例えば:
    
      ```ts
      const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
    
      type result = TupleToObject<typeof tuple> // expected { 'tesla': 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
      ```
    
      > GitHubで確認する:https://tsch.js.org/11/ja
    */
    
    /* _____________ ここにコードを記入 _____________ */
    
    type TupleToObject<T extends readonly (string | number | symbol)[]> = {
      [key in T[number]]: key
    }
    
    /* _____________ テストケース _____________ */
    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], {}]>
    
    /* _____________ 次のステップ _____________ */
    /*
      > 解答を共有する:https://tsch.js.org/11/answer/ja
      > 解答を見る:https://tsch.js.org/11/solutions
      > その他の課題:https://tsch.js.org/ja
    */

元の課題

type TupleToObject<T extends readonly any[]> = any
  1. まずany[]に想定される型を設定

    type TupleToObject<T extends readonly (string | number | symbol)[]> = any
    
  2. T[number] により、与えられるタプルからユニオン型を生成

    type TupleToObject<T extends readonly (string | number | symbol)[]> = {
      [T[number]]: key
    }
    
  3. [key in ...] が、生成されたユニオン型の各要素に対してイテレートをする

    type TupleToObject<T extends readonly (string | number | symbol)[]> = {
      [key in T[number]]: key
    }
    
  4. 結果として、オブジェクト型が生成される

[number]を使う理由:

  1. 全要素へのアクセス

配列やタプルの特定のインデックス(例:[0], [1])ではなく、全ての要素にアクセスしたい場合に使用します。

  1. 型の抽出

配列やタプルから、含まれうる全ての要素の型を一度に取得できます。

  1. 動的な長さへの対応

タプルや配列の長さが不定の場合でも、全ての要素の型を取得できます。

  1. ユニオン型の生成

タプルの場合、各要素のリテラル型のユニオンを生成します。これは、タプルの各要素を個別の型として扱いたい場合に有用です。

[number]の動作:

  • 配列の場合:配列の要素型を返します。
  • タプルの場合:タプルの全要素のリテラル型のユニオンを返します。
LeivyLeivy

00014-easy-first-of-array

    /*
      14 - First of Array
      -------
      by Anthony Fu (@antfu) #初級 #array
    
      ### 質問
    
      配列`T`を受け取り、その最初のプロパティの型を返す`First<T>`を実装します。
    
      例えば:
    
      ```ts
      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
      ```
    
      > GitHubで確認する:https://tsch.js.org/14/ja
    */
    
    /* _____________ ここにコードを記入 _____________ */
    
    type First<T extends any[]> = any
    
    /* _____________ テストケース _____________ */
    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' }>,
    ]
    
    /* _____________ 次のステップ _____________ */
    /*
      > 解答を共有する:https://tsch.js.org/14/answer/ja
      > 解答を見る:https://tsch.js.org/14/solutions
      > その他の課題:https://tsch.js.org/ja
    */

extends キーワードの役割

TypeScriptでは、extends は「型の制約」や「型の条件チェック」に使用されます。
A extends B は、「AがBの部分型(subset)であるか、またはBと同じ型であるか」をチェックします。
これを用いて、Tが配列であるかどうかをチェックし、配列が空であればnever配列が空でなければ配列(T)の0番目を返すようにする。

type First<T extends any[]> = T extends [] ? never : T[0];
LeivyLeivy

00018-easy-length-of-tuple

    /*
      18 - Length of Tuple
      -------
      by sinoon (@sinoon) #初級 #tuple
    
      ### 質問
    
      タプル`T`を受け取り、そのタプルの長さを返す型`Length<T>`を実装します。
    
      例えば:
    
      ```ts
      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
      ```
    
      > GitHubで確認する:https://tsch.js.org/18/ja
    */
    
    /* _____________ ここにコードを記入 _____________ */
    
    type Length<T> = any
    
    /* _____________ テストケース _____________ */
    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'>,
    ]
    
    /* _____________ 次のステップ _____________ */
    /*
      > 解答を共有する:https://tsch.js.org/18/answer/ja
      > 解答を見る:https://tsch.js.org/18/solutions
      > その他の課題:https://tsch.js.org/ja
    */
  1. まずは受け取るものが配列なので、extendsでTとany型の配列を比較する。その際、as constで読み取り専用になっているタプルに対応するため、readonlyを付ける。readonly を使用することで、通常の配列と readonly 配列の両方を受け入れることができる。
    type Length<T extends readonly any[]> = any
    
  2. Tの配列のlengthを返す。書き方は以下の通り
    type Length<T extends readonly any[]> = T['length']
    
LeivyLeivy

00043-easy-exclude

    /*
      43 - Exclude
      -------
      by Zheeeng (@zheeeng) #初級 #built-in #union
    
      ### 質問
    
      組み込みの型ユーティリティ`Exclude <T, U>`を使用せず、`U`に割り当て可能な型を`T`から除外する型を実装します。
    
      例えば:
    
      ```ts
      type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
      ```
    
      > GitHubで確認する:https://tsch.js.org/43/ja
    */
    
    /* _____________ ここにコードを記入 _____________ */
    
    type MyExclude<T, U> = any
    
    /* _____________ テストケース _____________ */
    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>>,
    ]
    
    /* _____________ 次のステップ _____________ */
    /*
      > 解答を共有する:https://tsch.js.org/43/answer/ja
      > 解答を見る:https://tsch.js.org/43/solutions
      > その他の課題:https://tsch.js.org/ja
    */
  1. これは、TUを比較して、一致するプロパティ(true)であればnever(除外)とし、falseであれば元のTのままにする
    type MyExclude<T, U> = T extends U ? never : T;
    
LeivyLeivy

00189-easy-awaited

    /*
      189 - Awaited
      -------
      by Maciej Sikora (@maciejsikora) #初級 #promise #built-in
    
      ### 質問
    
      Promise ライクな型が内包する型をどのように取得すればよいでしょうか。
    
      例えば:`Promise<ExampleType>`という型がある場合、どのようにして ExampleType を取得すればよいでしょうか。
    
      ```ts
      type ExampleType = Promise<string>
    
      type Result = MyAwaited<ExampleType> // string
      ```
    
      > この問題の元記事は [original article](https://dev.to/macsikora/advanced-typescript-exercises-question-1-45k4) by [@maciejsikora](https://github.com/maciejsikora) です。
    
      > GitHubで確認する:https://tsch.js.org/189/ja
    */
    
    /* _____________ ここにコードを記入 _____________ */
    
    type MyAwaited<T> = any
    
    /* _____________ テストケース _____________ */
    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>>,
    ]
    
    /* _____________ 次のステップ _____________ */
    /*
      > 解答を共有する:https://tsch.js.org/189/answer/ja
      > 解答を見る:https://tsch.js.org/189/solutions
      > その他の課題:https://tsch.js.org/ja
    */

TypeScript の MyAwaited<T> 型の解説

答えは以下

MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer U>
  ? U extends PromiseLike<any>
    ? MyAwaited<U>
    : U
  : never;

主要概念

1. PromiseLike

PromiseLike は Promise-like なオブジェクトを表す TypeScript の組み込み型です。

PromiseLike<T> {
  then<TResult1 = T, TResult2 = never>(
    onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
  ): PromiseLike<TResult1 | TResult2>;
}

PromiseLikethen メソッドを持つオブジェクトを表します。
Promise だけでなく、Promise-like な動作をする他のオブジェクトも含みます。
<T>Promise が解決される値の型を表します。

2. infer キーワード

infer は条件付き型の中で使用され、型の一部を推論して新しい型変数として使用できるようにします。
UnpackPromise<T> = T extends Promise<infer U> ? U : T;

// 使用例
type Result1 = UnpackPromise<Promise<string>>; // string
type Result2 = UnpackPromise<number>; // number

infer U は「ここに入る型をUとして推論せよ」という意味です。
上記の例では、Promise<string> から string を、Promise<number> から number を推論します。

MyAwaited<T> の詳細解説

T extends PromiseLike<any>

これは型パラメータ T の制約です。
TPromiseLike (Promise や thenable オブジェクト)である必要があります。

T extends PromiseLike<infer U>

条件付き型と infer キーワードを使用しています。
TPromiseLike であった場合に、その内部の型を U として推論します。

U extends PromiseLike<any> ? MyAwaited<U> : U

入れ子になった Promise を処理するための再帰的な定義です。
U が更に PromiseLike である場合、MyAwaited<U> を再帰的に呼び出します。
そうでない場合は U をそのまま返します。

最後の : never

これは型制約 T extends PromiseLike<any> を満たさない場合の型です。
実際にはこの行に到達することはありません(型制約により防がれているため)。

LeivyLeivy

00268-easy-if.ts

    /*
      268 - If
      -------
      by Pavel Glushkov (@pashutk) #初級 #utils
    
      ### 質問
    
      条件値`C`、 `C`が truthy である場合の戻り値の型`T`、`C`が falsy である場合の戻り値の型`F`を受け取る`If`を実装します。
      条件値`C` は`true`か`false`のどちらかであることが期待されますが、`T` と `F` は任意の型をとることができます。
    
      例えば:
    
      ```ts
      type A = If<true, 'a', 'b'>; // expected to be 'a'
      type B = If<false, 'a', 'b'>; // expected to be 'b'
      ```
    
      > GitHubで確認する:https://tsch.js.org/268/ja
    */
    
    /* _____________ ここにコードを記入 _____________ */
    
    type If<C, T, F> = any
    
    /* _____________ テストケース _____________ */
    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'>
    
    /* _____________ 次のステップ _____________ */
    /*
      > 解答を共有する:https://tsch.js.org/268/answer/ja
      > 解答を見る:https://tsch.js.org/268/solutions
      > その他の課題:https://tsch.js.org/ja
    */
  1. まずCはbooleanであるとわかっているので、If<C extends boolean, T, F>とする
  2. Cとtrueをextendsで比較し、trueであればT、falseであればFになる式を記述する
    type If<C extends boolean, T, F> = C extends true ? T : F; 
LeivyLeivy

00533-easy-concat

    /*
      533 - Concat
      -------
      by Andrey Krasovsky (@bre30kra69cs) #初級 #array
    
      ### 質問
    
      JavaScript の`Array.concat`関数を型システムに実装します。この型は 2 つの引数を受け取り、受け取ったイテレータの要素を順に含む新しい配列を返します。
    
      例えば:
    
      ```ts
      type Result = Concat<[1], [2]>; // expected to be [1, 2]
      ```
    
      > GitHubで確認する:https://tsch.js.org/533/ja
    */
    
    /* _____________ ここにコードを記入 _____________ */
    
    type Concat<T, U> = any
    
    /* _____________ テストケース _____________ */
    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>
    
    /* _____________ 次のステップ _____________ */
    /*
      > 解答を共有する:https://tsch.js.org/533/answer/ja
      > 解答を見る:https://tsch.js.org/533/solutions
      > その他の課題:https://tsch.js.org/ja
    */
  1. TとUをextendsでany[]と比較する
    type Concat<T extends any[], U extends any[]> = any

するとtype casesに以下のエラーが出る

    型 'readonly [1]' は制約 'any[]' を満たしていません。
    型 'readonly [1]' は 'readonly' であるため、変更可能な型 'any[]' に代入することはできません。
  1. エラーを解決するためにTupleの型をreadonlyで定義する
    type Tuple = readonly unknown[];
  1. TとUをanyではなくTuple型と比較する
    type Tuple = readonly unknown[];
    
    type Concat<T extends Tuple, U extends Tuple> = any
  1. 新しい配列を作る
    type Tuple = readonly unknown[];
    
    type Concat<T extends Tuple, U extends Tuple> = [...T,...U]
LeivyLeivy

00898-easy-includes

    /*
      898 - Includes
      -------
      by null (@kynefuk) #初級 #array
    
      ### 質問
    
      JavaScriptの`Array.include`関数を型システムに実装します。この型は、2 つの引数を受け取り、`true`や`false`を出力しなければなりません。
    
      例えば:
    
      ```ts
      type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`
      ```
    
      > GitHubで確認する:https://tsch.js.org/898/ja
    */
    
    /* _____________ ここにコードを記入 _____________ */
    
    type Includes<T extends readonly any[], U> = any
    
    /* _____________ テストケース _____________ */
    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>>,
    ]
    
    /* _____________ 次のステップ _____________ */
    /*
      > 解答を共有する:https://tsch.js.org/898/answer/ja
      > 解答を見る:https://tsch.js.org/898/solutions
      > その他の課題:https://tsch.js.org/ja
    */
  1. T[number](インデックス型アクセス(Indexed Access Types))を使用し、配列Tのすべての要素のユニオン型を取得します。例えば、['a', 'b', 'c']であれば、T[number]は 'a' | 'b' | 'c'となります。
  2. 次に、配列Tの要素をキーとして、すべての値をtrueに設定したマップ型を作成します。
    type Includes<T extends readonly any[], U> = {
      [key in T[number]]: true
    }
  1. この時点では、Includesは単にマップ型を返します。例えば、Includes<['a', 'b', 'c'], 'a'>は次の型を生成します。
{
  'a': true;
  'b': true;
  'c': true;
}
  1. 次に、このマップ型を使用して、UがTの要素に含まれているかをチェックします。
type Includes<T extends readonly any[], U> = {
  [key in T[number]]: true
}[U];

ここで、[U]はマップ型のキーアクセスを表し、UTに含まれていればtrueを返し、含まれていなければundefinedを返します。
5. 最後に、この結果をtrueまたはfalseに変換する必要があります。extendsを使用して、trueかどうかをチェックします。

type Includes<T extends readonly any[], U> = {
  [key in T[number]]: true
}[U] extends true ? true : false;
LeivyLeivy
  /*
    3057 - Push
    -------
    by jiangshan (@jiangshanmeta) #初級 #array

    ### 質問

    ```Array.push```のジェネリックバージョンを実装します。

    例えば:

    ```typescript
    type Result = Push<[1, 2], '3'> // [1, 2, '3']
    ```

    > GitHubで確認する:https://tsch.js.org/3057/ja
  */

  /* _____________ ここにコードを記入 _____________ */

  type Push<T extends unknown[], U> = [...T , U]

  /* _____________ テストケース _____________ */
  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]>>,
  ]

  /* _____________ 次のステップ _____________ */
  /*
    > 解答を共有する:https://tsch.js.org/3057/answer/ja
    > 解答を見る:https://tsch.js.org/3057/solutions
    > その他の課題:https://tsch.js.org/ja
  */
  1. まず、第一引数はunknownの配列なのでextendsで比較します
  type Push<T extends unknown[], U> = any;
  1. 次に、スプレッド構文で第二引数Uを追加した新しい配列を生成します
  type Push<T extends unknown[], U> = [...T , U]
LeivyLeivy

/*
3060 - Unshift

by jiangshan (@jiangshanmeta) #初級 #array

質問

Array.unshiftの型バージョンを実装します。

例えば:

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

GitHubで確認する:https://tsch.js.org/3060/ja
*/

/* _____________ ここにコードを記入 _____________ */

type Unshift<T extends unknown[], U> = [U, ...T];

/* _____________ テストケース _____________ */
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']>>,
]

/* _____________ 次のステップ _____________ /
/

解答を共有する:https://tsch.js.org/3060/answer/ja
解答を見る:https://tsch.js.org/3060/solutions
その他の課題:https://tsch.js.org/ja
*/

  1. まず第一引数はunknonwnの配列なのでTをextendsで指定する
  type Unshift<T extends unknown[], U> = any;
  1. 第二引数を先頭とした新しい配列をスプレッド構文で生成する
  type Unshift<T extends unknown[], U> = [U, ...T];
LeivyLeivy

/*
3312 - Parameters

by midorizemi (@midorizemi) #初級 #infer #tuple #built-in

質問

組み込みの型ユーティリティParameters<T>を使用せず、Tからタプル型を構築する型を実装します。

例えば:

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

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

GitHubで確認する:https://tsch.js.org/3312/ja
*/

/* _____________ ここにコードを記入 _____________ */

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

/* _____________ テストケース _____________ */
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>, []>>,
]

/* _____________ 次のステップ _____________ /
/

解答を共有する:https://tsch.js.org/3312/answer/ja
解答を見る:https://tsch.js.org/3312/solutions
その他の課題:https://tsch.js.org/ja
*/

  1. Tをinferキーワードを使って推論します。
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) 
  1. T が関数型である場合には、推論された引数型 P を返し、それ以外の場合には never を返します。
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;

infer キーワードの詳細解説

infer キーワードは、TypeScriptの条件型 (conditional types) と共に使われる特殊なキーワードで、型の一部を推論するために使用されます。
特に、型の一部を抽出したり、再利用する際に便利。

このスクラップは2024/09/14にクローズされました