Closed71

Types Challenge medium 記録

Kyosuke KuboKyosuke Kubo

Omit

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

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

type TodoPreview = MyOmit<Todo, "description" | "title">;

const todo: TodoPreview = {
  completed: false,
};

Kyosuke KuboKyosuke Kubo

excludeを作り、まずプロパティだけ削除してmapped typeで型を定義する

Kyosuke KuboKyosuke Kubo

Readonly 2

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

type MyReadonly2<T, K extends keyof T> = {
  readonly [P in K]: T[P];
} & Omit<T, K>;

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

Kをmapped typeで展開しながらreadonlyをつける。その上で、残ったかたをTとKからOmitを使って生成し、intersectionで融合

Kyosuke KuboKyosuke Kubo

DeepReadonly

Kyosuke KuboKyosuke Kubo
type X = {
  x: {
    a: 1;
    b: "hi";
  };
  y: "hey";
};

type DeepReadonly<T> = {
  readonly [key in keyof T]: T[key] extends object
    ? DeepReadonly<T[key]>
    : T[key];
};

type Todo = DeepReadonly<X>; // should be same as `Expected`
Kyosuke KuboKyosuke Kubo

基本的にconditional typeで条件分岐させてobjectかをチェックして、その後再起的に呼び出す。

Kyosuke KuboKyosuke Kubo

Chainable Options

Kyosuke KuboKyosuke Kubo
declare const config: Chainable;

type Chainable<T = {}> = {
  option<K extends string, V>(
    key: K,
    value: V
  ): Chainable<T & { [key in K]: V }>;
  get(): T;
};

const result = config
  .option("foo", 123)
  .option("name", "type-challenges")
  .option("bar", { value: "Hello World" })
  .get();

// expect the type of result to be:
interface Result {
  foo: number;
  name: string;
  bar: {
    value: string;
  };
}
Kyosuke KuboKyosuke Kubo

まず<T = {}>でdefault型引数を設定する。
その後メソッドを定義していき、options()Tにmergeしていく。get()で今までの型を吐き出す。

Kyosuke KuboKyosuke Kubo

Last of Array

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

type Last<T extends unknown[]> = T extends [...unknown[], infer L] ? L : never;

type tail1 = Last<arr1>; // expected to be 'c'
type tail2 = Last<arr2>; // expected to be 1

Kyosuke KuboKyosuke Kubo

Variadic Tuple Typesを使う必要がある。
any[] extends [...unknown[], infer L]とするとLが最後の要素として取得できる。

Kyosuke KuboKyosuke Kubo

Promise.All

Kyosuke KuboKyosuke Kubo

できたけどめちゃむずい

declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<{ [key in keyof T]: T[key] extends Promise<infer R> ? R : T[key] }>
Kyosuke KuboKyosuke Kubo

trim

Kyosuke KuboKyosuke Kubo
type Space = " " | "\n\t" | "\n" | "\t"
type TrimLeft<T extends string> = T extends `${Space}${infer L}` ? TrimLeft<L> : T
type TrimRight<T extends string> = T extends `${infer R}${Space}` ? TrimRight<R> : T
type Trim<T extends string> = TrimLeft<TrimRight<T>>
Kyosuke KuboKyosuke Kubo

case全部対応するためにSpaceの定義変えた

type cases = [
  Expect<Equal<Trim<'str'>, 'str'>>,
  Expect<Equal<Trim<' str'>, 'str'>>,
  Expect<Equal<Trim<'     str'>, 'str'>>,
  Expect<Equal<Trim<'str   '>, 'str'>>,
  Expect<Equal<Trim<'     str     '>, 'str'>>,
  Expect<Equal<Trim<'   \n\t foo bar \t'>, 'foo bar'>>,
  Expect<Equal<Trim<''>, ''>>,
  Expect<Equal<Trim<' \n\t '>, ''>>,
]
Kyosuke KuboKyosuke Kubo

ReplaceAll

Kyosuke KuboKyosuke Kubo

答え見たけど難しい

type ReplaceAll<S extends string, From extends string, To extends string> =  
  From extends ''? S: S extends `${infer F}${From}${infer L}`? `${F}${To}${ReplaceAll<L, From, To>}` : S
Kyosuke KuboKyosuke Kubo

Append Argument

Kyosuke KuboKyosuke Kubo
type AppendArgument<Fn extends (...args: any) => any, A> = Fn extends (...args: infer Args) => infer R ? (...args: [...Args, A]) => R : never
Kyosuke KuboKyosuke Kubo

(...args: any) => anyで関数の型になる

// スプレッド構文を型で使うときがいまいちわかりにくい

Kyosuke KuboKyosuke Kubo

length of string

Kyosuke KuboKyosuke Kubo
type LengthOfString<S extends string, Arr extends string[] = []> = S extends `${infer Head}${infer Rest}` ? LengthOfString<Rest, [Head, ...Arr]> : Arr['length']
Kyosuke KuboKyosuke Kubo

再帰でどんどんHeadの文字を呼び出して最終的に配列にして、Arr["length"]をする。

Kyosuke KuboKyosuke Kubo

Flatten

Kyosuke KuboKyosuke Kubo
type Flatten<Array extends any[]> = 
  Array extends [infer First, ...infer Last] ?  First extends any[] ? [...Flatten<First>, ...Flatten<Last>] : [First, ...Flatten<Last>] : [];
Kyosuke KuboKyosuke Kubo

最初から配列の要素とっていて、それも配列だったら再帰。そうでなければ、その要素とそれ以外の要素の再帰でFlatにする

Kyosuke KuboKyosuke Kubo

Append to Object

Kyosuke KuboKyosuke Kubo


type AppendToObject<TypeObject, KeyObject extends string, ValueObject> = {
  [key in keyof TypeObject | KeyObject]: key extends keyof TypeObject ? TypeObject[key] : ValueObject;
}

このスクラップは4ヶ月前にクローズされました