type-challenages
00002-medium-return-type
Get Return Type
type MyReturnType<T> = T extends (...args: any) => infer V ? V : T00003-medium-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
type MyExclude1<T, K> = T extends K ? never : T
type MyOmit<T, K> = {
[x in MyExclude1<keyof T, K>]: T[x]
}00004-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
type MyInclude1<T, K> = T extends K ? T : never
type MyPick<T, K extends keyof T> = {
[x in MyInclude1<keyof T, K>]: T[x]
}00005-extreme-readonly-keys
Implement a generic GetReadonlyKeys<T> that returns a union of the readonly keys of an Object.
type SelfEqual<T, R> = (<J>(J: J) => J extends T ? 1 : 2) extends (<J>(J: J) => J extends R ? 1 : 2) ? true : false
type GetReadonlyKeys<T extends Record<string, any>> = keyof {
[K in keyof T as (SelfEqual<{
[P in K]: T[P]
}, {
readonly [P in K]: T[P]
}> extends true ? K : never)]: T[K]
}00006-hard-simple-vue
In this challenge, we assume that SimpleVue take an Object with data, computed and methods fields as it's only argument,
datais a simple function that returns an object that exposes the contextthis, but you won't be accessible to other computed values or methods.computedis an Object of functions that take the context asthis, doing some calculation and returns the result. The computed results should be exposed to the context as the plain return values instead of functions.methodsis an Object of functions that take the context asthisas well. Methods can access the fields exposed bydata,computedas well as othermethods. The different betweencomputedis thatmethodsexposed as functions as-is.
The type of SimpleVue's return value can be arbitrary.
type GetComputedProperty<C> = C extends Record<string, (args: unknown[]) => unknown> ? {
[K in keyof C]: ReturnType<C[K]>
} : never
declare function SimpleVue<D, C extends Record<string, unknown>, M>(options: {
data: (this: {}) => D
computed: C & ThisType<D & GetComputedProperty<C> & M>
methods: M & ThisType<D & GetComputedProperty<C> & M>
}): any00007-easy-readonly
type MyReadonly<T> = {
readonly [K in keyof T]: T[K]
}00008-medium-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>.
type MyReadonly2<T, K extends keyof T = keyof T> = Omit<T, K> & { readonly [Key in K]: T[Key] }00009-medium-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.
type DeepReadonly<T> = {
readonly [k in keyof T]: T[k] extends never | (() => any) ? T[k] : DeepReadonly<T[k]>
}00010-medium-tuple-to-union
type Arr = ['1', '2', '3']
type Test = TupleToUnion<Arr> // expected to be '1' | '2' | '3'type TupleToUnion<T> = T extends Array<infer V> ? V : never00011-easy-tuple-to-object
For example:
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'}type TupleToObject<T extends readonly any[]> = {
[k in T[number]]: k
}00012-medium-chainable-options
For example
declare const config: Chainable
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
}
}type Chainable<T extends {}> = {
option<K extends string, V>(key: K, value: V): Chainable<Omit<T, K> & { [k in K]: V }>
get(): T
}00013-warm-hello-world
type HelloWorld = string // expected to be a string00014-easy-first
For example:
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 3type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never00015-medium-last
For example
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type tail1 = Last<arr1> // expected to be 'c'
type tail2 = Last<arr2> // expected to be 1type Last<T extends any[]> = T extends [infer F, ...infer Rest] ? Rest extends [] ? F : Last<Rest> : never00016-medium-pop
Implement a generic Pop<T> that takes an Array T and returns an Array without it's last element.
For example
type arr1 = ['a', 'b', 'c', 'd']
type arr2 = [3, 2, 1]
type re1 = Pop<arr1> // expected to be ['a', 'b', 'c']
type re2 = Pop<arr2> // expected to be [3, 2]type Pop<T extends any[]> = T extends [...infer Rest, infer Last] ? Rest : []00017-hard-currying-1
const add = (a: number, b: number) => a + b
const three = add(1, 2)
const curriedAdd = Currying(add)
const five = curriedAdd(2)(3)The function passed to Currying may have multiple arguments, you need to correctly type it.
type Curry<P, R> = P extends [infer H, ...infer T] ? (p: H) => Curry<T, R> : R
declare function Currying<T extends Function>(fn: T): T extends (...args: infer Arg) => infer Return
? Arg extends never[]
? () => Return
: Curry<Arg, Return>
: never00018-easy-tuple-length
For given a tuple, you need create a generic Length, pick the length of the tuple
For example:
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 5type Length<T> = T extends readonly unknown[] ? T['length'] : never00020-medium-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.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise<string>((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
// expected to be `Promise<[number, 42, string]>`
const p = PromiseAll([promise1, promise2, promise3] as const)// 手写Awaited<T>
type MyAwaited1<T> =
T extends null | undefined
? T
: T extends object & { then: (fulfilled: infer U) => any }
? U extends (value: infer F, ...args: any) => any
? F
: never
: T
declare function PromiseAll<T extends readonly unknown[] | []>(values: T): Promise<{
-readonly [K in keyof T]: MyAwaited1<T[K]>
}>00043-easy-exclude
Implement the built-in Exclude<T, U>
Exclude from
Tthose types that are assignable toU
For example:
type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'type MyExclude<T, U> = T extends U ? never : T00055-hard-union-to-intersection
Implement the advanced util type UnionToIntersection<U>
For example
type I = UnionToIntersection<'foo' | 42 | true> // expected to be 'foo' & 42 & true// 触发分派 > 函数参数交叉
type UnionToIntersection<U> = (U extends U ? (args: U) => never : never) extends (args: infer V) => never ? V : never00057-hard-get-required
Implement the advanced util type GetRequired<T>, which remains all the required fields
For example
type I = GetRequired<{ foo: number, bar?: string }> // expected to be { foo: number }type GetRequired<T> = {
[K in keyof T as T[K] extends { [K in keyof T]-?: T[K] }[K] ? K : never ]: T[K]
}00057-hard-get-required
Implement the advanced util type GetOptional<T>, which remains all the optional fields
For example
type I = GetOptional<{ foo: number, bar?: string }> // expected to be { bar?: string }type GetOptional<T> = {
[K in keyof T as T[K] extends { [K in keyof T]-?: T[K] }[K] ? never : K ]: T[K]
}00062-medium-type-lookup
Sometimes, you may want to look up a type in a union by its 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.
interface Cat {
type: 'cat'
breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}
interface Dog {
type: 'dog'
breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
color: 'brown' | 'white' | 'black'
}
type MyDogType = LookUp<Cat | Dog, 'dog'> // expected to be `Dog`type LookUp<U, T> = U extends { type: T } ? U : never00089-hard-required-keys
Implement the advanced util type RequiredKeys<T>, which picks all the required keys into a union.
For example
type Result = RequiredKeys<{ foo: number; bar?: string }>;
// expected to be “foo”type RequiredKeys<T> = keyof {
[K in keyof T as T[K] extends Required<T>[K] ? K : never]: T[K]
}00090-hard-optional-keys
Implement the advanced util type OptionalKeys<T>, which picks all the optional keys into a union.
type OptionalKeys<T> = keyof {
[K in keyof T as T[K] extends Required<T>[K] ? never : K]: never
}00106-medium-trimleft
Implement `TrimLeft<T>` which takes an exact string type and returns a new string with the whitespace beginning removed.
For example
```ts
type trimed = TrimLeft<' Hello World '> // expected to be 'Hello World '
```type WhiteSpace = ' ' | '\t' | '\n'
type TrimLeft<S extends string> = S extends `${WhiteSpace}${infer Rest}` ? TrimLeft<Rest> : S00108-medium-trim
Implement Trim<T> which takes an exact string type and returns a new string with the whitespace from both ends removed.
For example
type trimmed = Trim<' Hello World '> // expected to be 'Hello World'type WhiteSpace = ' ' | '\t' | '\n'
type Trim<S extends string> = S extends `${WhiteSpace}${infer T}` | `${infer T}${WhiteSpace}` ? Trim<T> : S00110-medium-capitalize
Implement Capitalize<T> which converts the first letter of a string to uppercase and leave the rest as-is.
For example
type capitalized = Capitalize<'hello world'> // expected to be 'Hello world'type MyCapitalize<S extends string> = S extends `${infer F}${infer R}`
? `${Uppercase<F>}${R}`
: S00112-hard-capitalizewords
Implement CapitalizeWords<T> which converts the first letter of *each word of a string* to uppercase and leaves the rest as-is.
For example
type capitalized = CapitalizeWords<'hello world, my friends'> // expected to be 'Hello World, My Friends'type CapitalizeWords<
S extends string,
W extends string = '',
> = S extends `${infer A}${infer B}`
? Uppercase<A> extends Lowercase<A>
? `${Capitalize<`${W}${A}`>}${CapitalizeWords<B>}`
: CapitalizeWords<B, `${W}${A}`>
: Capitalize<W>00114-hard-camelcase
Implement CamelCase<T> which converts snake_case string to camelCase.
For example
type camelCase1 = CamelCase<'hello_world_with_types'> // expected to be 'helloWorldWithTypes'
type camelCase2 = CamelCase<'HELLO_WORLD_WITH_TYPES'> // expected to be same as previous onetype CamelCase<S extends string> = S extends Lowercase<S> ?
S extends `${infer F}_${infer RF}${infer R}`
? RF extends Uppercase<RF>
? `${F}_${CamelCase<`${RF}${R}`>}`
: `${F}${Uppercase<RF>}${CamelCase<R>}`
: S
: CamelCase<Lowercase<S>>00116-medium-replace
Implement Replace<S, From, To> which replace the string From with To once in the given string S
For example
type replaced = Replace<'types are fun!', 'fun', 'awesome'> // expected to be 'types are awesome!'type Replace<S extends string, From extends string, To extends string> = From extends '' ? S : S extends `${infer L}${From}${infer A}` ? `${L}${To}${A}` : S00119-medium-replaceall
Implement ReplaceAll<S, From, To> which replace the all the substring From with To in the given string S
For example
type replaced = ReplaceAll<'t y p e s', ' ', ''> // expected to be 'types'type ReplaceAll<S extends string, From extends string, To extends string> =
From extends '' ? S
: S extends `${infer A}${From}${infer B}` ? `${A}${To}${ReplaceAll<B, From, To>}` : S00147-hard-c-printf-parser
There is a function in C language: printf. This function allows us to print something with formatting. Like this:
printf("The result is %d.", 42);This challenge requires you to parse the input string and extract the format placeholders like %d and %f. For example, if the input string is "The result is %d.", the parsed result is a tuple ['dec'].
Here is the mapping:
type ControlsMap = {
c: 'char',
s: 'string',
d: 'dec',
o: 'oct',
h: 'hex',
f: 'float',
p: 'pointer',
}type ControlsMap = {
c: 'char'
s: 'string'
d: 'dec'
o: 'oct'
h: 'hex'
f: 'float'
p: 'pointer'
}
type ParsePrintFormat<PrintFS, Res extends string[] = []> =
PrintFS extends `%${infer F extends keyof ControlsMap}${infer R}`
? ParsePrintFormat<R, [...Res, ControlsMap[F]]>
: PrintFS extends `%%${infer R}`
? ParsePrintFormat<R, Res>
: PrintFS extends `${string}${infer R}`
? ParsePrintFormat<R, Res>
: Res00151-extreme-query-string-parser
You're required to implement a type-level parser to parse URL query string into a object literal type.
Some detailed requirements:
- Value of a key in query string can be ignored but still be parsed to
true. For example,'key'is without value, so the parser result is{ key: true }. - Duplicated keys must be merged into one. If there are different values with the same key, values must be merged into a tuple type.
- When a key has only one value, that value can't be wrapped into a tuple type.
- If values with the same key appear more than once, it must be treated as once. For example,
key=value&key=valuemust be treated askey=valueonly.
// 你的答案
type Include<T extends unknown[], Search> = T extends [infer Item, ...infer Rest]
? Search extends Item
? true
: Include<Rest, Search>
: false
type AndStrParser<
Str extends string,
StrArr extends unknown[] = [],
> = Str extends `${infer Item}&${infer Rest}`
? AndStrParser<Rest, [...StrArr, Item]>
: [...StrArr, Str]
type NormalizeArr<T extends unknown[], Result extends unknown[] = []> = T extends [
infer Item,
...infer Rest,
]
? Item extends `${infer _}=${infer _}` | ''
? NormalizeArr<Rest, [...Result, Item]>
: NormalizeArr<Rest, [...Result, `${Item & string}=true`]>
: Result
type DetermineTrueStr<T> = T extends 'true' ? true : T
type ArrParser<Arr extends unknown[] = [], Result extends Record<string, any> = {}> = Arr extends [
infer Item,
...infer Rest,
]
? Item extends `${infer Key}=${infer Val}`
? ArrParser<
Rest,
{
[K in keyof Result | Key]: K extends Key
? Result[Key] extends string | true
? Result[Key] extends DetermineTrueStr<Val>
? DetermineTrueStr<Val>
: [Result[Key], DetermineTrueStr<Val>]
: Result[Key] extends unknown[]
? Include<Result[Key], DetermineTrueStr<Val>> extends true
? Result[Key]
: [...Result[Key], DetermineTrueStr<Val>]
: DetermineTrueStr<Val>
: Result[K]
}
>
: Result
: Result
type ParseQueryString<Str extends string> = ArrParser<NormalizeArr<AndStrParser<Str>>>00189-easy-awaited
If we have a type which is wrapped type like Promise. How we can get a type which is inside the wrapped type?
For example: if we have Promise<ExampleType> how to get ExampleType?
type ExampleType = Promise<string>
type Result = MyAwaited<ExampleType> // stringtype MyAwaited<T> = T extends PromiseLike<infer P> ? MyAwaited<P> : T00191-medium-append-argument
type Fn = (a: number, b: string) => number
type Result = AppendArgument<Fn, boolean>
// expected be (a: number, b: string, x: boolean) => numbertype AppendArgument<Fn, A> = Fn extends (...args: infer U) => infer R
? (...args: [...U, A]) => R
: Fn00213-hard-vue-basic-props
For example
props: {
foo: Boolean
}
// or
props: {
foo: { type: Boolean }
}should be inferred to type Props = { foo: boolean }.
When passing multiple constructors, the type should be inferred to a union.
props: {
foo: { type: [Boolean, Number, String] }
}
// -->
type Props = { foo: boolean | number | string }When an empty object is passed, the key should be inferred to any.
For more specified cases, check out the Test Cases section.
required,default, and array props in Vue are not considered in this challenge.
type ResultType<T> = T extends (...args: any[]) => infer R
? R
: T extends new (...args: unknown[]) => infer S
? S
: any
type PropsTypes<T> = {
[P in keyof T]: T[P] extends { type: infer Result }
? Result extends readonly unknown[]
? ResultType<Result[number]>
: ResultType<Result>
: ResultType<T[P]>
}
declare function VueBasicProps<
Props,
Data,
Computed extends Record<PropertyKey, (...args: unknown[]) => unknown>,
Methods,
>(options: {
props: Props
data(this: PropsTypes<Props>): Data
computed: Computed & ThisType<Data>
methods: Methods &
ThisType<
PropsTypes<Props> &
Methods & {
[P in keyof Computed]: ReturnType<Computed[P]>
}
>
}): unknown00216-extreme-slice
For example
type Arr = [1, 2, 3, 4, 5]
type Result = Slice<Arr, 2, 4> // expected to be [3, 4]// your answers
type Index = ['+', number] | ['-', number]
type FormatIndex<T extends number> = `${T}` extends `-${infer R extends number}`
? ['-', R]
: ['+', T]
type NumberToTuple<T extends number, Result extends 0[] = []> = Result['length'] extends T
? Result
: NumberToTuple<T, [0, ...Result]>
type MinusOne<T extends number, Result extends 0[] = NumberToTuple<T>> = Result extends [infer F, ...infer R]
? R['length']
: 0
type Minus<L extends number, M extends number> = L extends M
? 0
: M extends 0
? L
: Minus<MinusOne<L>, MinusOne<M>>
/**
* CorrectIndex<-1, 5> // 4
* CorrectIndex<1, 5> // 1
*/
type CorrectIndex<T extends number, L extends number = 0, I extends Index = FormatIndex<T>> = I[0] extends '+'
? I[1]
: Minus<L, I[1]>
type GT<T extends number, D extends number, E extends boolean = T extends D ? true : false> = E extends true ? false : T extends D
? true
: T extends 0
? false
: GT<MinusOne<T>, D, false>
type Slice<
Arr extends unknown[],
Start extends number = 0,
End extends number = Arr['length'],
CorrectStart extends number = CorrectIndex<Start, Arr['length']>,
CorrectEnd extends number = CorrectIndex<End, Arr['length']>,
Result extends unknown[] = [],
> = GT<CorrectStart, Arr['length']> extends true
? []
: CorrectEnd extends 0
? Result
: CorrectStart extends 0
? Arr extends [infer F, ...infer R]
? Slice<R, never, never, MinusOne<CorrectStart>, MinusOne<CorrectEnd>, [...Result, F]>
: Result
: Arr extends [infer F, ...infer R]
? Slice<R, never, never, MinusOne<CorrectStart>, MinusOne<CorrectEnd>, Result>
: never00223-hard-isany
So, let's write a utility type IsAny<T>, which takes input type T. If T is any, return true, otherwise, return false.
type IsAny<T> = boolean extends (T extends never ? true : false) ? true : false;00268-easy-if
Implement the util type If<C, T, F> which accepts condition C, a truthy value T, and a falsy value F. C is expected to be either true or false while T and F can be any type.
For example:
type A = If<true, 'a', 'b'> // expected to be 'a'
type B = If<false, 'a', 'b'> // expected to be 'b'type If<C, T, F> = C extends true ? T : F00270-hard-typed-get
For example
type Data = {
foo: {
bar: {
value: 'foobar',
count: 6,
},
included: true,
},
hello: 'world'
}
type A = Get<Data, 'hello'> // 'world'
type B = Get<Data, 'foo.bar.count'> // 6
type C = Get<Data, 'foo.bar'> // { value: 'foobar', count: 6 }type Split<S extends string> = S extends `${infer First}.${infer Rest}`
? [First, ...Split<Rest>]
: [S]
type Join<S extends string[]> = S extends [infer First, ...infer Rest]
? Rest['length'] extends 0
? First
: `${First}.${Join<Rest>}`
: ''
type Get<
T,
K extends string,
StrArr extends string[] = Split<K>,
> = K extends keyof T
? T[K]
: StrArr[0] extends keyof T
? StrArr['length'] extends 1
? T[StrArr[0]]
: Get<
T[StrArr[0]],
StrArr extends [string, ...infer Rest] ? Join<Rest> : '',
StrArr extends [string, ...infer Rest] ? Rest : []
>
: never00274-extreme-integers-comparator
Implement a type-level integers comparator. We've provided an enum for indicating the comparison result, like this:
- If a is greater than b, type should be Comparison.Greater.
- If a and b are equal, type should be Comparison.Equal.
- If a is lower than b, type should be Comparison.Lower.
Note that a and b can be positive integers or negative integers or zero, even one is positive while another one is negative.
type MyEqual<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false
type IsFNumber<T extends string | number> = `${T}` extends `-${number}` ? true : false
type GetNumber<T extends string | number> = `${T}` extends `-${infer N}` ? N : T
type NTCompare<T extends string | number, N extends string | number> = IsFNumber<T> extends IsFNumber<N> ? never : IsFNumber<T> extends true ? Comparison.Lower : Comparison.Greater
type GetNumberArray<T extends string | number, R extends number[] = []> = `${R['length']}` extends `${T}` ? R : GetNumberArray<T, [...R, 0]>
type EasyCompare<A extends string | number, B extends string | number> =
GetNumberArray<GetNumber<A>> extends [...GetNumberArray<GetNumber<B>>, ...number[]]
? IsFNumber<A> extends true ? Comparison.Lower : Comparison.Greater
: IsFNumber<A> extends true ? Comparison.Greater : Comparison.Lower;
type EComparator<A extends string | number, B extends string | number> = NTCompare<A, B> extends never ? MyEqual<A, B> extends true ? Comparison.Equal : EasyCompare<A, B> : NTCompare<A, B>
type Comparator<A extends string | number, B extends string | number> = NTCompare<A, B> extends never ? MyEqual<A, B> extends true ? Comparison.Equal :
IsFNumber<A> extends true ? PEasyCompare<GetNumberPArray<B>, GetNumberPArray<A>> : PEasyCompare<GetNumberPArray<A>, GetNumberPArray<B>> : NTCompare<A, B>
type GetNumberPArray<T extends number | string> = `${T}` extends `${infer F}${infer Rest}` ? [F, ...GetNumberPArray<Rest>] : []
type PEasyCompare<A extends Array<string | number>, B extends Array<string | number>> = EComparator<A['length'], B['length']> extends Comparison.Equal
? PCompare<A, B>
: EComparator<A['length'], B['length']>
type GetFirst<T extends any[]> = T[0];
type GetRest<T extends any[]> = T extends [infer F, ...infer Rest] ? Rest : never;
type PCompare<A extends any[], B extends any[]> = EComparator<GetFirst<A>, GetFirst<B>> extends Comparison.Equal ?
A['length'] extends 1 ? Comparison.Equal : PCompare<GetRest<A>, GetRest<B>> : EComparator<GetFirst<A>, GetFirst<B>>00296-medium-permutation
type perm = Permutation<'A' | 'B' | 'C'>; // ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']type Permutation<T, U = T> = [T] extends [never]
? []
: T extends U
? [T, ...Permutation<Exclude<U, T>>]
: []00298-medium-length-of-string
Compute the length of a string literal, which behaves like String#length.
type StringToArray<S extends string> = S extends `${infer F}${infer R}` ? [F, ...StringToArray<R>] : []
type LengthOfString<S extends string> = StringToArray<S>['length']00300-hard-string-to-number
Convert a string literal to a number, which behaves like Number.parseInt.
type ToNumber<S extends string> = S extends `${infer R extends number}` ? R : never00399-hard-tuple-filter
Implement a type FilterOut<T, F> that filters out items of the given type F from the tuple T.
For example,
type Filtered = FilterOut<[1, 2, null, 3], null> // [1, 2, 3]type FilterOut<T extends any[], U> = T extends [infer F, ...infer R]
? [F] extends [U]
? FilterOut<R, U>
: [F, ...FilterOut<R, U>]
: T00459-medium-flatten
In this challenge, you would need to write a type that takes an array and emitted the flatten array type.
For example:
type flatten = Flatten<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, 5]type Flatten<T> = T extends [] ? []
: T extends [infer F, ...infer A]
? [...Flatten<F>, ...Flatten<A>]
: [T]00462-extreme-currying-2
Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each take a single argument.
But in our daily life, currying dynamic arguments is also commonly used, for example, the Function.bind(this, [...params]) API.
const func = (a: number, b: number, c: number) => {
return a + b + c
}
const bindFunc = func(null, 1, 2)
const result = bindFunc(3) // result: 6Thus, based on Currying 1, we would need to have the dynamic argument version:
const add = (a: number, b: number, c: number) => a + b + c
const three = add(1, 1, 1)
const curriedAdd = DynamicParamsCurrying(add)
const six = curriedAdd(1, 2, 3)
const seven = curriedAdd(1, 2)(4)
const nine = curriedAdd(2)(3)(4)In this challenge, DynamicParamsCurrying may take a function with zero to multiple arguments, you need to correctly type it. The returned function may accept at least one argument. When all the arguments as satisfied, it should yield the return type of the original function correctly.
type PartialTuple<T extends any[], U extends T[number][] = []> = T extends [infer F, ...infer R] ? [...U, F] | PartialTuple<R, [...U, F]> : []
type Tails<T extends any[]> = T extends [any, ...infer R] ? R : never
type Rest<T extends any[], U extends any[], R extends any[] = []> = R['length'] extends U['length'] ? T : Rest<Tails<T>, U, [...R, unknown]>
type Curry<T extends any[], R, Args extends any[] = PartialTuple<T>> = <TargetArgs extends Args>(...args: TargetArgs) => (TargetArgs['length'] extends T['length'] ? R : Curry<Rest<T, TargetArgs>, R>)
declare function DynamicParamsCurrying<T extends any[], R>(fn: (...args: T) => R): Curry<T, R>00472-hard-tuple-to-enum-object
The enum is an original syntax of TypeScript (it does not exist in JavaScript). So it is converted to like the following form as a result of transpilation:
let OperatingSystem;
(function (OperatingSystem) {
OperatingSystem[OperatingSystem["MacOS"] = 0] = "MacOS";
OperatingSystem[OperatingSystem["Windows"] = 1] = "Windows";
OperatingSystem[OperatingSystem["Linux"] = 2] = "Linux";
})(OperatingSystem || (OperatingSystem = {}));In this question, the type should convert a given string tuple to an object that behaves like an enum. Moreover, the property of an enum is preferably a pascal case.
Enum<["macOS", "Windows", "Linux"]>
// -> { readonly MacOS: "macOS", readonly Windows: "Windows", readonly Linux: "Linux" }If true is given in the second argument, the value should be a number literal.
Enum<["macOS", "Windows", "Linux"], true>
// -> { readonly MacOS: 0, readonly Windows: 1, readonly Linux: 2 }A1
// answer 1
type GetTupleUntilTarget<T extends readonly string[], S extends string> =
T extends readonly [infer F, ...infer R extends readonly string[]]
? S extends F
? []
: [F, ...GetTupleUntilTarget<R, S>]
: []
type Enum<T extends readonly string[], N extends boolean = false> =
N extends false
? {
readonly [P in T[number] as Capitalize<P>]: P
}
: {
readonly [K in T[number] as Capitalize<K>]: GetTupleUntilTarget<T, K>['length']
}A2
// TODO
type PascalCase<T extends string> = Capitalize<T>
type StringToNumber<T> = T extends `${infer N extends number}` ? N : never
type Enum<T extends readonly string[], N extends boolean = false> = {
[P in keyof T as T[P] extends string ? PascalCase<T[P]> : never]:
N extends true
? StringToNumber<P>
: T[StringToNumber<P>]
}00476-extreme-sum
Implement a type Sum<A, B> that summing two non-negative integers and returns the sum as a string. Numbers can be specified as a string, number, or bigint.
For example,
type T0 = Sum<2, 3> // '5'
type T1 = Sum<'13', '21'> // '34'
type T2 = Sum<'328', 7> // '335'
type T3 = Sum<1_000_000_000_000n, '123'> // '1000000000123'// sum the lengths of three arrays
type ArrToSum<T extends any[], U extends any[], S extends any[]> = `${[
...T,
...U,
...S,
]['length']}`
// convert a number string (e.g., "1", "21") to an array with the corresponding length
type StrToArr<T extends string, U extends never[] = []> = T extends ''
? []
: `${U['length']}` extends `${T}`
? U
: StrToArr<T, [...U, never]>
// sum single digits (e.g., A = "1", B = "2", C="5")
type SumDigit<A extends string, B extends string, C extends string> =
ArrToSum<StrToArr<LastDigit<A>>, StrToArr<LastDigit<B>>, StrToArr<C>>
// extract the last digit (e.g., extract "4" from "1234")
type LastDigit<T extends string> = T extends `${infer P}${infer Q}`
? Q extends ''
? P
: LastDigit<Q>
: ''
// extract the digits other than the laast digit (e.g., extract "123" from "1234")
type RestDigits<T extends string> = T extends ''
? ''
: T extends `${infer P}${infer Q}`
? Q extends ''
? ''
: `${P}${RestDigits<Q>}`
: ''
// main calculation logic. takes A, B, and Carry.
// Carry represents the carry digit from previous digit calculation.
// the calculation is done recursively, digit by digit.
type _Sum<
A extends string,
B extends string,
Carry extends string = '0',
> = `${A}${B}${Carry}` extends ''
? ''
: `${_Sum<
RestDigits<A>,
RestDigits<B>,
RestDigits<SumDigit<A, B, Carry>>
>}${LastDigit<SumDigit<A, B, Carry>>}`
type Sum<
A extends string | number | bigint,
B extends string | number | bigint,
> = _Sum<`${A}`, `${B}`>00517-extreme-multiply
Implement a type Multiply<A, B> that multiplies two non-negative integers and returns their product as a string. Numbers can be specified as string, number, or bigint.
For example:
type T0 = Multiply<2, 3> // '6'
type T1 = Multiply<3, '5'> // '15'
type T2 = Multiply<'4', 10> // '40'
type T3 = Multiply<0, 16> // '0'
type T4 = Multiply<'13', '21'> // '273'
type T5 = Multiply<'43423', 321543n> // '13962361689'type IsIncludesZero<T> = [Extract<T, 0 | '0'>] extends [never] ? false : true
type MultiplyWithSmallNumber<
A extends string | number | bigint,
B extends string | number | bigint,
AC extends unknown[] = [],
T extends string = '0',
> = `${A}` extends `${infer X extends number}`
? X extends AC['length']
? T
: MultiplyWithSmallNumber<A, B, [unknown, ...AC], Sum<T, B>>
: never
type MultiplyNoZero<
A extends string | number | bigint,
B extends string | number | bigint,
T extends string = '0',
Add extends string = '',
> = ReverseString<`${A}`> extends `${infer F extends number}${infer R}`
? MultiplyNoZero<ReverseString<R>, B, Sum<`${MultiplyWithSmallNumber<F, B>}${Add}`, T>, `${Add}0`>
: T
type Multiply<
A extends string | number | bigint,
B extends string | number | bigint,
> = IsIncludesZero<A | B> extends true ? '0' : MultiplyNoZero<A, B>00527-medium-append-to-object
For example
type Test = { id: '1' }
type Result = AppendToObject<Test, 'value', 4> // expected to be { id: '1', value: 4 }type AppendToObject<T extends object, U extends string, V> = {
[P in keyof T | U]: P extends U
? V
: (P extends keyof T ? T[P] : never)
}00529-medium-absolute
For example
type Test = -100
type Result = Absolute<Test> // expected to be "100"type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer U}` ? U : `${T}`00531-medium-string-to-union
type Test = "123"
type Result = StringToUnion<Test> // expected to be "1" | "2" | "3"type StringToUnion<T extends string> = T extends `${infer A}${infer R}`
? A | StringToUnion<R> : never00533-easy-concat
For example:
type Result = Concat<[1], [2]> // expected to be [1, 2]type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]00545-hard-printf
Implement Format<T extends string> generic.
For example,
type FormatCase1 = Format<"%sabc"> // FormatCase1 : string => string
type FormatCase2 = Format<"%s%dabc"> // FormatCase2 : string => number => string
type FormatCase3 = Format<"sdabc"> // FormatCase3 : string
type FormatCase4 = Format<"sd%abc"> // FormatCase4 : stringtype FormatType = { s: string, d: number, f: number/*, ...*/ };
type Format<T extends string> = T extends `${any}%${infer S}${infer R}` ?
S extends keyof FormatType ? (arg: FormatType[S]) => Format<R> : Format<R> :
string;00553-hard-deep-object-to-unique
For example,
import { Equal } from "@type-challenges/utils"
type Foo = { foo: 2; bar: { 0: 1 }; baz: { 0: 1 } }
type UniqFoo = DeepObjectToUniq<Foo>
declare let foo: Foo
declare let uniqFoo: UniqFoo
uniqFoo = foo // ok
foo = uniqFoo // ok
type T0 = Equal<UniqFoo, Foo> // false
type T1 = UniqFoo["foo"] // 2
type T2 = Equal<UniqFoo["bar"], UniqFoo["baz"]> // false
type T3 = UniqFoo["bar"][0] // 1
type T4 = Equal<keyof Foo & string, keyof UniqFoo & string> // truetype DeepObjectToUniq<
O extends object,
_UniqFlag extends [PropertyKey?, object?] = []
> = {
[P in keyof O]: O[P] extends object ? DeepObjectToUniq<O[P], [P, O]> : O[P]
} & { [flag: symbol]: _UniqFlag }00599-medium-merge
Merge two types into a new type. Keys of the second type overrides keys of the first type.
For example
type foo = {
name: string
age: string
}
type coo = {
age: number
sex: string
}
type Result = Merge<foo, coo> // expected to be {name: string, age: number, sex: string}type Merge<F, S> = {
[key in keyof F | keyof S]: key extends keyof S
? S[key]
: key extends keyof F
? F[key]
: never
}00612-medium-kebabcase
Replace the camelCase or PascalCase string with kebab-case.
FooBarBaz -> foo-bar-baz
For example
type FooBarBaz = KebabCase<"FooBarBaz">
const foobarbaz: FooBarBaz = "foo-bar-baz"
type DoNothing = KebabCase<"do-nothing">
const doNothing: DoNothing = "do-nothing"type KebabCase<S> = S extends `${infer S1}${infer S2}`
? S2 extends Uncapitalize<S2>
? `${Uncapitalize<S1>}${KebabCase<S2>}`
: `${Uncapitalize<S1>}-${KebabCase<S2>}`
: S00645-medium-diff
Get an Object that is the difference between O & O1
type Diff<O, O1> = Omit<O & O1, keyof O & keyof O1>;