Closed55

Types challenge easy 記録

Kyosuke KuboKyosuke Kubo

Pick

Kyosuke KuboKyosuke Kubo
interface Todo {
  title: string
  description: string
  completed: boolean
}

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

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

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

console.log(todo);
Kyosuke KuboKyosuke Kubo

回答できた。

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

ジェネリクスの部分、Tのプロパティに含まれるものだけをKとして許容するようにする。必然的にTはobjectになる。

オブジェクト内部は[]で囲み、mapped typeを使って繰り返し処理。
Pはプロパティで各valueの部分にオブジェクトのpropsを変数だから[]で囲みT[P]とすることで、繰り返し分値が展開される。

Kyosuke KuboKyosuke Kubo

Readonly

Kyosuke KuboKyosuke Kubo

回答できた。

interface Todo {
  title: string
  description: string
}
type MyReadonly<T> = {
    readonly [P in keyof T]: T[P];
};

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
Kyosuke KuboKyosuke Kubo

今回はtype T = Readonly<{~~~~}>を使わずに実装する。

今回as constと各propsの前につけるreadonlyが思いついたので、一応どっちがいいか気になって調べた。

https://chaika.hatenablog.com/entry/2020/08/28/083000

↑上記参考資料によると、Readonlyは直下のpropsしか見ない模様。一方でas const では配列のpush なども型エラーにしてくれる模様。

Kyosuke KuboKyosuke Kubo

Tuple to Object

Kyosuke KuboKyosuke Kubo

解答できた。

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

// 問題がstringなので一応stringにした。
type TupleToObject<T extends readonly string[]> = {
    [V in T[number]]: V;
}

type result = TupleToObject<typeof tuple> 
Kyosuke KuboKyosuke Kubo

arrayにas constをつけた値をtypeofで出すと下記のようになる。

const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
type tuple = typeof tuple // readonly ['tesla', 'model 3', 'model X', 'model Y']

配列のまま型を生成することが可能になる。

Kyosuke KuboKyosuke Kubo

加えて、any[][number]でindexで回すみたいなことが可能になる。

// Tをreadonlyの配列のみを許容するようにする。
type TupleToObject<T extends readonly string[]> = {
    [V in T[number]]: V;
}

T[number]でindexしつつ、mapped typeを使って配列の値をVで取り出すことで、objectに変換。

Kyosuke KuboKyosuke Kubo

First of Array

Kyosuke KuboKyosuke Kubo

回答できた。最初できたと思ってissue見たらconditional typeがなぜ使われているのかがわからなかったため、再度考え以下のように実装。

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

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

type head1 = First<arr1>; // expected to be 'a'
type head2 = First<arr2>; // expected to be 3

const value1: head1 = "a";
const value2: head2 = 3;
console.log(value1);
console.log(value2);
Kyosuke KuboKyosuke Kubo
type First<T extends unknown[]> = T extends [] ? never : T[0];

まずGenericsで何でもいいから配列がくるようにして、空配列だったらnever、そうでなければT[0]で配列の先頭の値を型として持たせる。

Kyosuke KuboKyosuke Kubo

conditional typeが出てきたので、復習。

例えばこんな感じだったら、T extends U ? nerver : T;TがUに代入できたら、nerver。そうでなければT。

Kyosuke KuboKyosuke Kubo

neverは値を持たない型。
値が入った瞬間にコンパイルエラーになる。

Kyosuke KuboKyosuke Kubo

Length of Tuple

Kyosuke KuboKyosuke Kubo
type tesla = ["tesla", "model 3", "model X", "model Y"];
type spaceX = [
  "FALCON 9",
  "FALCON HEAVY",
  "DRAGON",
  "STARSHIP",
  "HUMAN SPACEFLIGHT"
];
type Length<T extends unknown[]> = T extends [] ? never : T["length"];

type teslaLength = Length<tesla>; // expected 4
type spaceXLength = Length<spaceX>; // expected 5
Kyosuke KuboKyosuke Kubo

Exclude

Kyosuke KuboKyosuke Kubo

回答できた。意外と簡単。

type HandmadeExclude<T, K> = T extends K ? never : T;
const value: HandmadeExclude<"a" | "b" | "c", "a" | "c"> = "b";
console.log(value);
Kyosuke KuboKyosuke Kubo

"a" | "b" | "c"みたいに複数を取りうる型だったとしてもmapped typeを使う必要はない。
extendsでできる。

一つの型を生成する際には必要ないが、プロパティを生成するしたり反復するような型を定義する際には必要

Kyosuke KuboKyosuke Kubo

awaited

Kyosuke KuboKyosuke Kubo

これは解答見ないとわからなかった。
こんな感じ?

type Awaited<T> = T extends Promise<infer U> ? U : never;
Kyosuke KuboKyosuke Kubo

↓以下みたいな感じでできる。
PromiseにPromiseがラップされていたときに使える。

type MyAwaited<T extends Promise<any>> = 
  T extends Promise<infer U> ? 
    U extends Promise<any> ? MyAwaited<U> : U 
  : never
Kyosuke KuboKyosuke Kubo

typeofの挙動で違和感を感じたが、引用で理解した。

注意しなければいけないのは、JavaScript の typeof は、TypeScript のタイプアノテーションで指定した型を返してくれるわけではないということです。 typeof が返す型情報は、JavaScript コードとして実行したときに、実際にその値がどのような型で扱われているかを示すものです。 なので、TypeScript 独自のタイプアノテーション情報(Tuple 型など)が調べられるわけではありません。 TypeScript のタイプアノテーションは、JavaScript へのトランスパイル時に使われる参考情報でしかありません。

https://maku.blog/p/ods6iv8/

Kyosuke KuboKyosuke Kubo

includes

Kyosuke KuboKyosuke Kubo
type Includes<T extends unknown[], K> = K extends T[number] ? true : false;
type isPillarMen = Includes<["Kars", "Esidisi", "Wamuu", "Santana"], "Dio">;
Kyosuke KuboKyosuke Kubo

Push

Kyosuke KuboKyosuke Kubo

この辺は慣れかも、配列に対する処理としてスプレッド構文使えそうかまず考えてみてもいいかも。

複雑な処理はいらない気がする。

Kyosuke KuboKyosuke Kubo

parameter

Kyosuke KuboKyosuke Kubo
type Params<T extends (...args: any) => any> = T extends (
  ...args: infer U
) => any
  ? U
  : never;

Kyosuke KuboKyosuke Kubo

Parameters<Type>がそもそもタプルで返してくれることに気づかなかった。

このスクラップは2021/12/08にクローズされました