Type Scriptの型システムについて学んでみたいと思ったのでType Challengeに挑戦してきました。(中級まで)
以降は私がType Challengeに挑戦したときに作成した回答です。
easy
// Pick
type MyPick<T, K extends keyof T> = { [key in K]: T[key] };
// Readonly
type MyReadonly<T> = { readonly [key in keyof T]: T[key] };
// TupleToObject
type TupleToObject<T extends readonly string[]> = { [key in T[number]]: key };
// First
type First1<T extends any[]> = T extends [infer F, ...any[]] ? F : never;
type First2<T extends any[]> = T[number] extends never ? never : T[0];
// Length of Tuple
type Length<T extends readonly any[]> = T['length'];
// Exclude
type MyExclude<T, U> = T extends U ? never : T;
// Awaited
type Awaited<T extends Promise<any>> = T extends Promise<infer R> ? R : never;
// If
type If<C extends boolean, T, F> = C extends false ? F : T;
// Concat
type Concat<T extends any[], U extends any[]> = [...T, ...U];
// Includes
type Includes<T extends readonly any[], U> = T extends [infer F, ...infer R] ? ([U, F] extends [F, U] ? true : Includes<R, U>) : false;
// Push
type Push<T extends any[], U> = [...T, U];
// Unshift
type Unshift<T extends any[], U> = [U, ...T];
medium
// Get Return Type
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// Omit
type MyOmit1<T, K> = { [key in keyof T as key extends K ? never : key]: T[key] };
type MyOmit2<T, K> = { [key in Exclude<keyof T, K>]: T[key] };
// Rreadonly 2
type MyReadonly2<T, K extends keyof T = keyof T> = { readonly [key in K]: T[key] } & { [key in Exclude<keyof T, K>]: T[key] };
// DeepReadonly
type DeepReadonly<T> = { readonly [key in keyof T]: T[key] extends { [key: string]: unknown } ? DeepReadonly<T[key]> : T[key] };
// Tuple to Union
type TupleToUnion<T extends readonly any[]> = T[number];
// Chainable Options
type Chainable<T = {}> = {
option<K extends string, V>(key: K, value: V): Chainable<{ [key in K]: V } & T>;
get(): T;
};
// Last of Array
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
// Pop
type Pop<T extends any[]> = T extends [...infer R, any] ? R : never;
// Promise.all
declare function PromiseAll<T extends any[]>(
values: readonly [...T]
): Promise<
{
[key in keyof T]: T[key] extends Promise<infer L> ? L : T[key];
}
>;
// Type Lookup
// type LookUp<U extends Animal, T extends U['type']> = T extends 'cat' ? Cat : Dog;
type LookUp<U, T> = U extends { type: T } ? U : never;
// Trim Left
type TrimLeft<S extends string> = S extends `${' ' | '\n' | '\t'}${infer SS}` ? TrimLeft<SS> : S;
// Trim
type TrimRight<S> = S extends `${infer SS}${' ' | '\n' | '\t'}` ? TrimRight<SS> : S;
type Trim1<S extends string> = TrimRight<TrimLeft<S>>;
type Trim2<S extends string> = S extends `${infer L}${' ' | '\n' | '\t'}` | `${' ' | '\n' | '\t'}${infer R}` ? Trim2<R | L> : S;
// Capitalize
type Capitalize2<S extends string> = S extends `${infer F}${infer S}` ? `${Uppercase<F>}${S}` : S;
// Replace
type Replace<S extends string, From extends string, To extends string> = '' extends From
? S
: S extends `${infer F}${From}${infer R}`
? `${F}${To}${R}`
: S;
// ReplaceAll
type ReplaceAll<S extends string, From extends string, To extends string> = '' extends From
? S
: S extends `${infer F}${From}${infer R}`
? `${F}${To}${ReplaceAll<R, From, To>}`
: S;
// Append Argument
// https://dev.to/macsikora/advanced-typescript-exercises-question-4-495c
type AppendArgument<Fn extends (...args: any[]) => any, A> = Fn extends (...args: infer T) => infer R ? (...args: [...T, A]) => R : never;
// Permutation
// https://github.com/type-challenges/type-challenges/issues/614
type Permutation<T, K = T> = [T] extends [never] ? [] : K extends K ? [K, ...Permutation<Exclude<T, K>>] : never;
// Length of String
type StringToTuple<S extends string> = S extends `${infer F}${infer R}` ? [F, ...StringToTuple<R>] : [];
type LengthOfString<S extends string> = StringToTuple<S>['length'];
// Flatten
type Flatten<T extends any[]> = T extends [infer F, ...infer R]
? F extends any[]
? [...Flatten<F>, ...Flatten<R>]
: [F, ...Flatten<R>]
: [];
// ...never を含むタプル型の計算結果はneverになる
type B = [1, 2, 3, ...never];
// Append to Object
type AppendToObject<T, U extends string, V> = { [key in U | keyof T]: key extends keyof T ? T[key] : V };
// Absolute
type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer R}` ? R : `${T}`;
// String to Union
type StringToUnion<T extends string> = T extends `${infer F}${infer R}` ? F | StringToUnion<R> : never;
// Merge
type Merge<F, S> = {
[key in keyof F | keyof S]: key extends keyof S ? S[key] : key extends keyof F ? F[key] : never;
};
// Camel Case
type CamelCase<S extends string> = S extends `${infer F1}${infer F2}${infer R}`
? F1 extends '-'
? F2 extends '-'
? `${F1}${CamelCase<`${F2}${R}`>}`
: F2 extends Uppercase<F2>
? `${F1}${Uppercase<F2>}${CamelCase<R>}`
: `${Uppercase<F2>}${CamelCase<R>}`
: `${F1}${CamelCase<`${F2}${R}`>}`
: S;
// Kebab Case
type Alpha =
| 'a'
| 'b'
| 'c'
| 'd'
| 'e'
| 'f'
| 'g'
| 'h'
| 'i'
| 'j'
| 'k'
| 'l'
| 'm'
| 'n'
| 'o'
| 'p'
| 'q'
| 'r'
| 's'
| 't'
| 'u'
| 'v'
| 'w'
| 'x'
| 'y'
| 'z'
| 'A'
| 'B'
| 'C'
| 'D'
| 'E'
| 'F'
| 'G'
| 'H'
| 'I'
| 'J'
| 'K'
| 'L'
| 'M'
| 'N'
| 'O'
| 'P'
| 'Q'
| 'R'
| 'S'
| 'T'
| 'U'
| 'V'
| 'W'
| 'X'
| 'Y'
| 'Z';
type KebabCase<S extends string> = S extends `${infer F1}${infer F2}${infer R}`
? F2 extends Alpha
? F2 extends Uppercase<F2>
? `${Lowercase<F1>}-${KebabCase<`${F2}${R}`>}`
: `${Lowercase<F1>}${KebabCase<`${F2}${R}`>}`
: `${Lowercase<F1>}${KebabCase<`${F2}${R}`>}`
: Lowercase<S>;
// Diff
type Diff<O, O1> = {
[key in Exclude<keyof O, keyof O1> | Exclude<keyof O1, keyof O>]: key extends keyof O ? O[key] : key extends keyof O1 ? O1[key] : never;
};
// AnyOf
type AnyOf<T extends readonly any[]> = T extends [infer F, ...infer R]
? F extends 0 | '' | false
? AnyOf<R>
: [] extends F
? AnyOf<R>
: keyof F extends never
? AnyOf<R>
: true
: false;
// IsNever
type IsNever<T> = [T] extends [never] ? true : false;
// IsUnion
type IsUnion1<T, U = T> = T[] extends never[] ? false : T extends T ? (Exclude<U, T>[] extends never[] ? false : true) : never;
type IsUnion2<T, U = T> = T extends T ? (U | T extends U & T ? false : true) : never;
type IsUnion3<T> = (<D>() => D extends [T] ? 1 : 2) extends <D>() => D extends (T extends T ? [T] : never) ? 1 : 2 ? false : true;
// ReplaceKeys
type ReplaceKeys<U, T extends string, Y extends {}> = U extends U
? {
[key in keyof U]: key extends T ? (key extends keyof Y ? Y[key] : never) : U[key];
}
: never;
// RemoveIndexSignature
type RemoveIndexSignature<T> = {
[key in keyof T as string extends key ? never : number extends key ? never : key]: T[key];
};
// PercentageParser
type PercentageParser<A extends string> = A extends `${infer S}${infer R}`
? S extends '+' | '-'
? R extends `${infer N}%`
? [S, N, '%']
: [S, R, '']
: A extends `${infer V}%`
? ['', V, '%']
: ['', A, '']
: ['', '', ''];
// DropChar
type DropChar<S, C extends string> = S extends `${infer F}${C}${infer R}` ? `${F}${DropChar<R, C>}` : S;
// MinusOne
type T10<T extends any[]> = [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T];
type DigitToArray<T extends string, R extends any[] = []> = `${R['length']}` extends T ? R : DigitToArray<T, [1, ...R]>;
type StringToArray<T extends string, R extends any[] = []> = T extends `${infer F}${infer E}`
? StringToArray<E, [...T10<R>, ...DigitToArray<F>]>
: R;
type MinusOne<T extends number> = StringToArray<`${T}`> extends [any, ...infer R] ? R['length'] : 0;
// PickByType
type PickByType<T, U> = {
[key in keyof T as T[key] extends U ? key : never]: T[key];
};
// StartsWith
type StartsWith<T extends string, U extends string> = T extends `${U}${infer R}` ? true : false;
// EndsWith
type EndsWith<T extends string, U extends string> = T extends `${infer R}${U}` ? true : false;
// PartialByKeys
type PartialByKeys<T, K = keyof T> = {
[key in keyof T as key extends K ? never : key]: T[key];
} &
{
[key in keyof T as key extends K ? key : never]?: key extends keyof T ? T[key] : never;
} extends infer I
? {
[key in keyof I]: I[key];
}
: never;
// RequiredByKeys
type RequiredByKeys<T, K = keyof T> = {
[key in keyof T as key extends K ? never : key]: T[key];
} &
{
[key in keyof T as key extends K ? key : never]-?: T[key];
} extends infer I
? {
[key in keyof I]: I[key];
}
: never;
// Mutable
type Mutable<T> = {
-readonly [key in keyof T]: T[key];
};
// OmitByType
type OmitByType<T, U> = {
[key in keyof T as T[key] extends U ? never : key]: T[key];
};
// ObjectEntries
type ObjectEntries<T> = {
[key in keyof T]-?: [key, T[key] extends infer F | undefined ? F : T[key]];
}[keyof T];
// Shift
type Shift<T extends any[]> = T extends [any, ...infer R] ? R : [];
// TupleToNestedObject
type TupleToNestedObject<T extends any[], U> = T extends [infer F, ...infer R] ? { [key in F & string]: TupleToNestedObject<R, U> } : U;
// Reverse
type Reverse<T extends any[]> = T extends [infer F, ...infer R] ? [...Reverse<R>, F] : [];
// FlipArguments
type FlipArguments<T> = T extends (...args: infer A) => infer R ? (...args: Reverse<A>) => R : never;