Open83

type challengesをやる

tskytsky

easy

Pick

Implement the built-in Pick<T, K> generic without using it.
Constructs a type by picking the set of properties K from T

https://github.com/type-challenges/type-challenges/tree/main/questions/00004-easy-pick

tskytsky

Tuple to Object

Given an array, transform it into an object type and the key/value must be in the provided array.

https://github.com/type-challenges/type-challenges/tree/main/questions/00011-easy-tuple-to-object

tskytsky

解答

type TupleToObject<T extends readonly (keyof any)[]> = {
  [K in T[number]]: K 
}

コメント

T extends readonly (keyof any)[]のようにプロパティのキーにできる型のみで制約をかける必要あり
T[number]で配列Tの要素の型をUnionでとれる

tskytsky

Awaited

If we have a type which is wrapped type like Promise. How we can get a type which is inside the wrapped type?

https://github.com/type-challenges/type-challenges/tree/main/questions/00189-easy-awaited

tskytsky

解答

type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer I>
  ? I extends PromiseLike<any>
    ? MyAwaited<I>
    : I
  : T;

コメント

PromiseLikeでthenableなオブジェクトに対応することと、ネストしているPromiseへの対応がちょっと面倒

tskytsky

Includes

Implement the JavaScript Array.includes function in the type system. A type takes the two arguments. The output should be a boolean true or false.

https://github.com/type-challenges/type-challenges/tree/main/questions/00898-easy-includes

tskytsky

解答

type Includes<T extends readonly any[], U> = T extends [infer F, ...infer Rest]
  ? Equal<F, U> extends true
    ? true
    : Includes<Rest, U>
  : false;

コメント

このあたりのテストケースが通らなさ過ぎてEqualを使ってしまった

  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>>,

一応recommendedのラベルがついている解答を見たところ同じような解答だった
https://github.com/type-challenges/type-challenges/issues/1568

急に難易度が上がったような 🤔

tskytsky

Omit

Implement the built-in Omit<T, K> generic without using it.
Constructs a type by picking all properties from T and then removing K

https://github.com/type-challenges/type-challenges/blob/main/questions/00003-medium-omit

tskytsky

解答

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

コメント

Excludeを使ってTのプロパティからKを取り除く
v4.1から導入されたasを使うことでインラインで書けるらしい

type MyOmit<T, K extends keyof T> = {
  [P in keyof T as P extends K ? never : P]: T[P];
};

https://github.com/type-challenges/type-challenges/issues/448

tskytsky

Readonly 2

Implement a generic MyReadonly2<T, K> which takes two type argument T and K.

K specify the set of properties of T that should set to Readonly. When K is not provided, it should make all properties readonly just like the normal Readonly<T>.

https://github.com/type-challenges/type-challenges/blob/main/questions/00008-medium-readonly-2

tskytsky

解答

type MyReadonly2<T extends object, K extends keyof T = keyof T> = Readonly<
  Pick<T, K>
> &
  Omit<T, Extract<keyof T, K>>;

コメント

Omitの中のExtractはいらなかったかも

tskytsky

Deep Readonly

Implement a generic DeepReadonly<T> which make every parameter of an object - and its sub-objects recursively - readonly.

You can assume that we are only dealing with Objects in this challenge. Arrays, Functions, Classes and so on do not need to be taken into consideration. However, you can still challenge yourself by covering as many different cases as possible.

https://github.com/type-challenges/type-challenges/blob/main/questions/00009-medium-deep-readonly

tskytsky

Chainable Options

Chainable options are commonly used in Javascript. But when we switch to TypeScript, can you properly type it?

In this challenge, you need to type an object or a class - whatever you like - to provide two function option(key, value) and get(). In option, you can extend the current config type by the given key and value. We should about to access the final result via get.

https://github.com/type-challenges/type-challenges/blob/main/questions/00012-medium-chainable-options

tskytsky

解答

type Chainable<C extends object = {}> = {
  option<K extends keyof any, V>(
    key: K extends keyof C ? never : K,
    value: V
  ): Chainable<Omit<C, K> & { [P in K]: V }>;
  get(): C;
};

コメント

K extends keyof C ? never : Kで現在定義されているプロパティを見て、定義済みであればkey: neverとしてエラーにする必要がある

tskytsky

Promise.all

Type the function PromiseAll that accepts an array of PromiseLike objects, the returning value should be Promise<T> where T is the resolved result array.

https://github.com/type-challenges/type-challenges/blob/main/questions/00020-medium-promise-all

tskytsky

解答 ❌

declare function PromiseAll<T extends any[]>(
  values: T
): Promise<{ [P in keyof T]: Awaited<T[P]> }>;

readonlyになってほしくないのになってしまったりtupleがarrayとして推論されたりで通らず

コメント

敗因:Variadic Tuple Typesを知らなかった ([...T])
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#variadic-tuple-types

この機能を使うことで次のように書くことができる

declare function PromiseAll<T extends any[]>(
  values: readonly [...T]
): Promise<{ [P in keyof T]: Awaited<T[P]> }>;

readonly [...T]T extends any[]によって、readonlyなタプルも渡せるようにしつつ、T自体はreadonlyでない、かつタプルはタプルのまま推論させるといったことができるみたいな感じ

tskytsky

Type Lookup

Sometimes, you may want to lookup for a type in a union to by their attributes.

In this challenge, we would like to get the corresponding type by searching for the common type field in the union Cat | Dog. In other words, we will expect to get Dog for LookUp<Dog | Cat, 'dog'> and Cat for LookUp<Dog | Cat, 'cat'> in the following example.

https://github.com/type-challenges/type-challenges/blob/main/questions/00062-medium-type-lookup

tskytsky

解答

type LookUp<U extends { type: string }, T extends U["type"]> = U extends {
  type: T;
}
  ? U
  : never;

コメント

パット見結構使いどころありそうだな~と思ったけど実例が思いつきませんでした

tskytsky

Trim Left

Implement TrimLeft<T> which takes an exact string type and returns a new string with the whitespace beginning removed.

https://github.com/type-challenges/type-challenges/blob/main/questions/00106-medium-trimleft

tskytsky

Trim

Implement Trim<T> which takes an exact string type and returns a new string with the whitespace from both ends removed.

https://github.com/type-challenges/type-challenges/blob/main/questions/00108-medium-trim

tskytsky

解答

type Space = " " | "\n" | "\t";
type Trim<S extends string> = S extends `${Space}${infer R}`
  ? Trim<R>
  : S extends `${infer L}${Space}`
  ? Trim<L>
  : S;

コメント

反省を生かしてSpaceを定義した
TrimLeftした後TrimRightすればよい

tskytsky

Capitalize

Implement Capitalize<T> which converts the first letter of a string to uppercase and leave the rest as-is.

https://github.com/type-challenges/type-challenges/blob/main/questions/00110-medium-capitalize

tskytsky

解答

type MyCapitalize<S extends string> = S extends `${infer S}${infer R}`
  ? `${Uppercase<S>}${R}`
  : "";

コメント

長さ1以上のstring Sに対してS extends ${infer S}${infer R}とするとinfer Sは最初の文字にマッチするので、あとは組み込みの型であるUppercase<S>を使って大文字にすればOK

tskytsky

ReplaceAll

Implement ReplaceAll<S, From, To> which replace the all the substring From with To in the given string S

https://github.com/type-challenges/type-challenges/blob/main/questions/00119-medium-replaceall

tskytsky

解答

type ReplaceAll<
  S extends string,
  From extends string,
  To extends string
> = From extends ``
  ? S
  : S extends `${infer L}${From}${infer R}`
  ? `${L}${To}${ReplaceAll<`${R}`, From, To>}`
  : S;

コメント

Replace同様左からマッチさせていき、残り部分(R)を再帰する

tskytsky

Append Argument

For given function type Fn, and any type A (any in this context means we don't restrict the type, and I don't have in mind any type 😉) create a generic type which will take Fn as the first argument, A as the second, and will produce function type G which will be the same as Fn but with appended argument A as a last one.

https://github.com/type-challenges/type-challenges/blob/main/questions/00191-medium-append-argument

tskytsky

解答

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

コメント

easyのPushParameterの組み合わせ

tskytsky

Permutation

Implement permutation type that transforms union types into the array that includes permutations of unions.

https://github.com/type-challenges/type-challenges/blob/main/questions/00296-medium-permutation

tskytsky

解答 ❌

Exclude使って再帰するのかな~くらいまでしかわからず

コメント

K extends KでUnionのそれぞれの型を評価するのと、[T] extends [never]によるneverのチェックがポイントっぽい

type Permutation<T, K = T> = [T] extends [never]
  ? []
  : K extends K
  ? [K, ...Permutation<Exclude<T, K>>]
  : never;

詳しい解説は以下
https://github.com/type-challenges/type-challenges/issues/614

tskytsky

Length of String

Compute the length of a string literal, which behaves like String#length.

https://github.com/type-challenges/type-challenges/blob/main/questions/00298-medium-length-of-string

tskytsky

解答

type StrToTuple<
  S extends string,
  T extends any[] = []
> = S extends `${infer F}${infer L}` ? StrToTuple<L, [...T, F]> : T;
type LengthOfString<S extends string> = StrToTuple<S>["length"];

コメント

tupleのlengthはとれるので変換したら行けた

tskytsky

Flatten

In this challenge, you would need to write a type that takes an array and emitted the flatten array type.

https://github.com/type-challenges/type-challenges/blob/main/questions/00459-medium-flatten

tskytsky

解答

type Flatten<Array> = Array extends [infer F, ...infer R]
  ? [...Flatten<F>, ...Flatten<R>]
  : Array extends []
  ? []
  : [Array];

コメント

lengthが1以上のタプルであれば各要素に対して再帰、空のタプルならそのまま返し、タプル以外であれば1要素のタプルとして返す

tskytsky

Append to object

Implement a type that adds a new field to the interface. The type takes the three arguments. The output should be an object with the new field.

https://github.com/type-challenges/type-challenges/blob/main/questions/00527-medium-append-to-object

tskytsky

解答

type AppendToObject<T extends object, U extends keyof any, V> = {
  [P in keyof T | U]: P extends keyof T ? T[P] : V;
};

コメント

用意されているテストケースだったらこれでも通りそうだなと思いましたが通りませんでした...

type AppendToObject<T extends object, U extends keyof any, V> = T & {
  [P in U]: V;
};
tskytsky

Absolute

Implement the Absolute` type. A type that take string, number or bigint. The output should be a positive number string

https://github.com/type-challenges/type-challenges/blob/main/questions/00529-medium-absolute

tskytsky

解答

type Absolute<T extends number | string | bigint> =
  `${T}` extends `${infer F}${infer R}` ? (F extends "-" ? R : `${T}`) : `${T}`;

コメント

↓が模範解答

type Absolute<T extends number | string | bigint> =
  `${T}` extends `-${infer R}` ? R : `${T}`;
tskytsky

String to Union

Implement the String to Union type. Type take string argument. The output should be a union of input letters

https://github.com/type-challenges/type-challenges/blob/main/questions/00531-medium-string-to-union

tskytsky

解答

type StringToTuple<T extends string> = T extends `${infer F}${infer R}` ? [F, ...StringToTuple<R>] : []
type StringToUnion<T extends string> = StringToTuple<T>[number];

コメント

↓模範解答

type StringToUnion<T extends string> = T extends `${infer Letter}${infer Rest}`
  ? Letter | StrintToUnion<Rest>
  : never;
tskytsky

Merge

Merge two types into a new type. Keys of the second type overrides keys of the first type.

https://github.com/type-challenges/type-challenges/blob/main/questions/00599-medium-merge

tskytsky

解答

type Merge<F, S> = {
  [P in Exclude<keyof F, keyof S> | keyof S]: P extends keyof S
    ? S[P]
    : P extends keyof F
    ? F[P]
    : never;
};

コメント

Sを優先して参照する

type Merge<F, S> = Omit<F, keyof S> & S;

だとなぜかテストが通らず(推論される型としてはあってそうなんですが)

tskytsky

KebabCase

Replace the camelCase or PascalCase string with kebab-case.

FooBarBaz -> foo-bar-baz

https://github.com/type-challenges/type-challenges/blob/main/questions/00612-medium-kebabcase

tskytsky

解答

type K = "-" | "_";
type _Kebab<S> = S extends `${infer L}${infer R}`
  ? L extends Lowercase<L> | K
    ? `${L}${_Kebab<R>}`
    : `-${Lowercase<L>}${_Kebab<R>}`
  : S;
type KebabCase<S> = _Kebab<S> extends `-${infer R}`
  ? R extends ""
    ? S
    : R
  : _Kebab<S>;

コメント

Uncapitalizeが完全に頭から抜けていた
https://github.com/type-challenges/type-challenges/issues/664

tskytsky

Object

Get an Object that is the difference between O & O1

https://github.com/type-challenges/type-challenges/blob/main/questions/00645-medium-diff

tskytsky

AnyOf

Implement Python liked any function in the type system. A type takes the Array and returns true if any element of the Array is true. If the Array is empty, return false.

https://github.com/type-challenges/type-challenges/blob/main/questions/00949-medium-anyof

tskytsky

解答

type IsFalsy<T> = T extends [] | undefined | null | "" | 0 | false
  ? true
  : T extends object
  ? [keyof T] extends [never]
    ? true
    : false
  : false;
type AnyOf<T extends readonly any[]> = [
  {
    [K in keyof T]: IsFalsy<T[K]> extends true ? never : true;
  }[number]
] extends [never]
  ? false
  : true;

コメント

TのUnionがFalsyな型のUnionのサブセットかどうかを見ればよいらしい めっちゃスマート

type AnyOf<T extends any[]> = T[number] extends
  | 0
  | ""
  | false
  | []
  | { [key: string]: never }
  | undefined
  | null
  ? false
  : true;

https://github.com/type-challenges/type-challenges/issues/954