/**
* 等号(`=`)で区切られた単一のキー-値ペアをオブジェクト型に変換する
*
* @typeParam T - `=` で区切られたキー-値ペア
*/
type ParseEq<T extends string> = T extends `${infer U}=${infer V}` ? { [k in U]: V } : { [k in T]: true };
type UniqueTupleInnerLoop<Heads extends any[], Tail> = Tail extends Heads[number]
? Heads extends [...infer U, infer V]
? UniqueTupleInnerLoop<U, V>
: []
: Heads extends [...infer U, infer V]
? [...UniqueTupleInnerLoop<U, V>, Tail]
: [Tail];
/**
* タプルに含まれる重複した要素を削除する
*/
type UniqueTuple<T extends any[]> = T extends [...infer U, infer V] ? UniqueTupleInnerLoop<U, V> : T;
/**
* タプルが1つの要素しか持たない場合、その要素の型を取り出す
*/
type StripTuple<T extends any[]> = T extends [infer U, ...infer V] ? V extends [] ? U : T : never;
/**
* オブジェクト型をマージする
*/
type Cat<Head, Tail> = {
[K in keyof Head | keyof Tail]: K extends keyof Head & keyof Tail
? Head[K] extends any[]
? UniqueTuple<[...Head[K], Tail[K]]>
: StripTuple<UniqueTuple<[Head[K], Tail[K]]>>
: K extends keyof Head
? Head[K]
: K extends keyof Tail
? Tail[K]
: never;
};
/**
* アンパサンド(`&`)で区切られた複数のキー-値ペアをオブジェクト型に変換する
*/
type SplitAmp<T extends string, Heads> = T extends `${infer U}&${infer V}` ? SplitAmp<V, Cat<Heads, ParseEq<U>>> : Cat<Heads, ParseEq<T>>;
/**
* クエリ文字列をパースして型にする
*/
type ParseQueryString<T extends string> = T extends '' ? {} : SplitAmp<T, {}>;
Solution by frodo821 #35233
type Push<
KVPair extends string,
R extends Record<string, unknown[]> = {},
K extends string = KVPair extends `${infer Key}=${string}` ? Key : KVPair,
V = KVPair extends `${string}=${infer Value}` ? Value : true
> = K extends keyof R
? V extends R[K][number]
? R
: { [P in keyof R]: P extends K ? [...R[P], V] : R[P] }
: { [P in keyof R | K]: P extends K ? [V] : R[P] }
type Transform<R extends Record<string, unknown[]>> = {
[P in keyof R]: R[P]['length'] extends 1 ? R[P][0] : R[P]
}
type ParseQueryString<S extends string, R extends Record<string, unknown[]> = {}> = S extends `${infer First}&${infer Rest}`
? ParseQueryString<Rest, Push<First, R>>
: S extends ''
? {}
: Transform<Push<S, R>>
Solution by 2083335157 #35109
// extract keys as union from query string
type ExtractKey<Q extends string> = Q extends `${infer L}&${infer R}`
? L extends `${infer Key}=${string}`
? Key | ExtractKey<R>
: L | ExtractKey<R>
: Q extends `${infer Key}=${string}`
? Key
: Q extends ""
? never
: Q;
// add item to tuple if not contained
type AddIfNotExists<T extends unknown[], U> = U extends T[number] ? T : [...T, U];
// extract values as tuple from query string
type ExtractValue<
Q extends string,
Key extends string,
Result extends unknown[] = []
> = Q extends `${string}${Key}&${infer Rest}`
? ExtractValue<Rest, Key, AddIfNotExists<Result, true>>
: Q extends `${string}${Key}=${infer Value}&${infer Rest}`
? ExtractValue<Rest, Key, AddIfNotExists<Result, Value>>
: Q extends `${string}${Key}`
? AddIfNotExists<Result, true>
: Q extends `${string}${Key}=${infer Value}`
? AddIfNotExists<Result, Value>
: Result;
type ParseQueryString<Q extends string> = {
[P in ExtractKey<Q>]: ExtractValue<Q, P> extends infer U ? (U extends [infer E] ? E : U) : never;
};
Solution by yukicountry #34533
// your answers
type Split<
S extends string,
Sp extends string
> = S extends `${infer L}${Sp}${infer R}` ? [L, ...Split<R, Sp>] : [S];
type ParseKV<
S extends string,
K_V extends string[] = Split<S, "=">
> = K_V extends [infer K extends string, infer V extends string]
? [K, V]
: K_V extends [infer K extends string]
? [K, true]
: never;
type OpList<T extends string[]> = T extends [
infer H extends string,
...infer R extends string[]
]
? [ParseKV<H>, ...OpList<R>]
: [];
type GetAllKeys<T extends object[]> = T extends [
infer F,
...infer R extends object[]
]
? keyof F | GetAllKeys<R>
: never;
type SetKV<
T extends Record<string, any>,
K extends string,
V extends any = true
> = {
[P in keyof T | K]: P extends K
? P extends keyof T
? T[P] extends string | true
? Equal<T[P], V> extends true
? T[P]
: [T[P], V]
: V extends T[P][number]
? T[P]
: [...T[P], V]
: V
: T[P];
};
type ParseQueryString<
T extends string,
U extends [string, any][] = OpList<Split<T, "&">>,
Res extends Record<GetAllKeys<U>, any> = {}
> = T extends ""
? {}
: U extends [
infer F extends [string, any],
...infer R extends [string, any][]
]
? ParseQueryString<T, R, SetKV<Res, F[0], F[1]>>
: Res;
Solution by chenqy-yh #34350
type AddValue<O extends object, K extends keyof O, V> = {
[key in keyof O] : key extends K
? O[key] extends any[]
? V extends O[key][number]
? O[key]
: [...O[key], V]
: O[key] extends V
? O[key]
: [O[key], V]
: O[key]
}
type ParseValue<T extends string, L extends object> = T extends `${infer K}=${infer V}`
? K extends keyof L
? AddValue<L, K, V>
: L & Record<K, V>
: T extends keyof L
? AddValue<L, T, true>
: L & Record<T, true>
type _ParseQueryString<S extends string, L extends object = {}> = S extends `${infer F}&${infer R}`
? _ParseQueryString<R, ParseValue<F, L>>
: ParseValue<S, L>
type ParseQueryString<S extends string> = S extends ''
? {}
: Omit<_ParseQueryString<S>, never>
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<ParseQueryString<''>, {}>>,
Expect<Equal<ParseQueryString<'k1'>, { k1: true }>>,
Expect<Equal<ParseQueryString<'k1&k1'>, { k1: true }>>,
Expect<Equal<ParseQueryString<'k1&k2'>, { k1: true, k2: true }>>,
Expect<Equal<ParseQueryString<'k1=v1'>, { k1: 'v1' }>>,
Expect<Equal<ParseQueryString<'k1=v1&k1=v2'>, { k1: ['v1', 'v2'] }>>,
Expect<Equal<ParseQueryString<'k1=v1&k2=v2'>, { k1: 'v1', k2: 'v2' }>>,
Expect<Equal<ParseQueryString<'k1=v1&k2=v2&k1=v2'>, { k1: ['v1', 'v2'], k2: 'v2' }>>,
Expect<Equal<ParseQueryString<'k1=v1&k2'>, { k1: 'v1', k2: true }>>,
Expect<Equal<ParseQueryString<'k1=v1&k1=v1'>, { k1: 'v1' }>>,
Expect<Equal<ParseQueryString<'k1=v1&k1=v2&k1=v1'>, { k1: ['v1', 'v2'] }>>,
Expect<Equal<ParseQueryString<'k1=v1&k2=v1&k1=v2&k1=v1'>, { k1: ['v1', 'v2'], k2: 'v1' }>>,
Expect<Equal<ParseQueryString<'k1=v1&k2=v2&k1=v2&k1=v3'>, { k1: ['v1', 'v2', 'v3'], k2: 'v2' }>>,
Expect<Equal<ParseQueryString<'k1=v1&k1'>, { k1: ['v1', true] }>>,
Expect<Equal<ParseQueryString<'k1&k1=v1'>, { k1: [true, 'v1'] }>>,
]
Solution by rimo030 #33334
// your answers
type Contains<T extends readonly any[],V> = V extends T[number] ? true : false;
type IsEqual<S,U> = (<T>() => T extends S ? 1 : 2) extends (<T>() => T extends U ? 1 : 2) ? true : false ;
//precondition of usage: for key in keyof U, U[key] is not tuple type or array type.
type Combine<T ,U> = {
[key in (keyof T | keyof U)]:key extends keyof T ?
key extends keyof U ?
T[key] extends readonly any[] ?
Contains<T[key],U[key]> extends true ?
T[key]
:
[...T[key],U[key]]
:
IsEqual<T[key],U[key]> extends true ?
T[key]
:
[T[key],U[key]]
:T[key]
:key extends keyof U ?
U[key]
:
"!!!@@@### unreachable type!"
}
type ParseKeyOrPair<keyOrPair,Parsed> =
keyOrPair extends `${infer key}=${infer value}` ?
Combine<Parsed,{[k in key]:value}>
:
keyOrPair extends `${infer keyOnly}` ?
keyOnly extends "" ?
Parsed
:
Combine<Parsed,{[k in keyOnly]:true}>
:
Parsed
;
type ParseQueryString<T extends string,Parsed = {}> =
T extends "" ?
Parsed
:
T extends `${infer keyOrPair}&${infer remaining}` ?
ParseQueryString<remaining,ParseKeyOrPair<keyOrPair,Parsed>>
:
T extends `${infer key}` ?
ParseKeyOrPair<key,Parsed>
:
Parsed;
;
Solution by sciencefunq #32921
type QueryValue<S extends string> = S extends `${infer K extends string}=${infer V extends string}` ? [K, V] : S extends '' ? [] : [S, true];
type SpliteQuery<S extends string> = S extends `${infer P1 extends string}&${infer P2 extends string}` ?
[...SpliteQuery<P1>, ...SpliteQuery<P2>] :
[QueryValue<S>];
type ForceUniquePush<A extends any | any[], T> = A extends any[] ? (T extends A[number] ? A : [...A, T]) : [A, T];
type Iterator<K extends string, R extends Record<K, any>, V extends any> = {
[key in K | Exclude<keyof R, K>]: key extends K ? (V extends R[key] ? R[key] : ForceUniquePush<R[key], V>) : R[key]
}
type CombineValues<L extends [string, any][], R extends Record<string, any> = {}> =
L extends [infer E extends [string, any], ...infer O extends [string, any][]] ?
E extends [infer K extends string, infer V extends any] ?
R extends Record<K, any> ?
CombineValues<O, Iterator<K, R, V>> :
CombineValues<O, {[key in keyof R | K]: key extends K ? V : R[key]}> :
R :
L extends [[infer K extends string, infer V]] ?
R extends Record<K, any> ?
Iterator<K, R, V> :
{[key in keyof R | K]: key extends K ? V : R[key]} :
R;
type ParseQueryString<S extends string> = CombineValues<SpliteQuery<S>>;
Solution by alexbidenko #32800
type AppendValue<
T extends (string | true)[] | true | string | unknown,
V
> = T extends any[]
? V extends T[number]
? T
: [...T, V]
: T extends string | true
? V extends T
? T
: [T, V]
: V;
type RecordKV<
K extends string,
V extends string | true,
T extends Record<string, (string | true)[] | string | true> = {}
> = { [key in keyof T | K]: key extends K ? AppendValue<T[key], V> : T[key] };
type ParseKV<
P extends string,
T extends Record<string, (string | true)[] | string | true> = {}
> = P extends `${infer K}=${infer V}`
? RecordKV<K, V, T>
: P extends ""
? T
: RecordKV<P, true, T>;
type ParseQueryString<
Q extends string,
T extends Record<string, (string | true)[] | string | true> = {}
> = Q extends `${infer P}&${infer R}`
? ParseQueryString<R, ParseKV<P, T>>
: ParseKV<Q, T>;
Solution by vangie #32342
type Norm<T> = T extends object ? { [K in keyof T]: T[K] } : T;
type AddToValue<C, V> = C extends V
? C
: C extends {}[]
? V extends C[number]
? C
: [...C, V]
: [C, V];
type AddToObject<R, K extends string, V> = K extends keyof R
? {
[K1 in keyof R]: K1 extends K ? AddToValue<R[K1], V> : R[K1];
}
: Norm<R & { [Key in K]: V }>;
type ParseItem<S extends string, R> = S extends `${infer K}=${infer V}`
? AddToObject<R, K, V>
: AddToObject<R, S, true>;
type ParseQueryString<S extends string, R = {}> = S extends ""
? R
: S extends `${infer I}&${infer T}`
? ParseQueryString<T, ParseItem<I, R>>
: ParseItem<S, R>;
Solution by alexandroppolus #32281
类型体操怎么这么难啊🥲
type SetObject<K extends string, V> = {
[k in K]: V
}
type Pair<T extends string> = T extends `${infer K}=${infer V}`
? SetObject<K, V>
: SetObject<T, true>
type ToArray<T> = T extends unknown[] ? T : [T]
type Push<T extends object, P extends object> = {
[K in keyof P]: K extends keyof T
? P[K] extends ToArray<T[K]>[number]
? T[K]
: [...ToArray<T[K]>, P[K]]
: P[K]
} & Omit<T, keyof P>
type Core<
T extends string,
O extends object = {}
> = T extends `${infer A}&${infer B}`
? Core<B, Push<O, Pair<A>>>
: T extends ''
? O
: Push<O, Pair<T>>
type Prettify<T> = {
[K in keyof T]: T[K]
} & {}
type ParseQueryString<T extends string> = Prettify<Core<T>>
Solution by Conard-Ferenc #30227
type Merge<T> = {
[K in keyof T]: T[K]
}
type MergeQuery<T extends string, Result extends Record<PropertyKey, unknown>> = T extends `${infer K}=${infer V}`
? K extends keyof Result
? Result[K] extends unknown[]
? V extends Result[K][number]
? Result
: Omit<Result, K> & { [_K in K]: [...Result[K], V] }
: Result[K] extends V
? Result
: Omit<Result, K> & { [_K in K]: [Result[K], V] }
: Result & { [_K in K]: V }
: T extends ''
? Result
: T extends keyof Result
? Result[T] extends true
? Result
: Omit<Result, T> & { [_K in T]: [Result[T], true] }
: Result & { [_K in T]: true }
type ParseQuery<T extends string, Result extends Record<PropertyKey, unknown> = {}> = T extends `${infer F}&${infer L}`
? ParseQuery<L, MergeQuery<F, Result>>
: MergeQuery<T, Result>
type ParseQueryString<T extends string> = Merge<ParseQuery<T>>
Solution by NameWjp #29924
type ParseParam<T extends string> = T extends `${infer A}=${infer B}` ? {
[K in A]: B
} : T extends `${infer A}` ? { [K in A]: true } : {}
type MergeValue<One, Other> = One extends Other ? One : Other extends unknown[] ? Remove<[One, ...Other]> : [One, Other]
type Remove<T extends any[], L extends any[] = []> = T extends [infer A, ...infer B] ? A extends L[number] ? Remove<B, L> : Remove<B, [...L, A]> : L
type MergeParams<One, Other> = {
[K in keyof One | keyof Other]:
K extends keyof One ?
K extends keyof Other ?
MergeValue<One[K], Other[K]>
: One[K]
: K extends keyof Other ? Other[K] : never
}
type ParseQueryString<S extends string> = S extends '' ? {} : S extends `${infer A}&${infer B}` ? MergeParams<ParseParam<A>, ParseQueryString<B>> : ParseParam<S>
Solution by tclxshunquan-wang #29914
// your answers
type GetKey<S> = S extends `${infer Key}=${string}` ? Key : S
type GetValue<S> = S extends `${string}=${infer Value}` ? Value : true
type GetUq<T, V> = T extends unknown[] ?
V extends T[number] ? T : [...T, V]
: T extends V ? T : [T, V]
type ParseQueryString<S, R extends object = {}> = S extends `${infer Head}&${infer Rest}` ? ParseQueryString<Rest, {
[K in keyof R | GetKey<Head>]:
K extends keyof R
? K extends GetKey<Head>
? GetUq<R[K], GetValue<Head>>
: R[K]
: GetValue<Head>
}> : S extends `${infer Last}` ? Last extends '' ? R : ParseQueryString<`${Last}&`, R> : R
Solution by 437204933 #29740
// First is about merging some parsed result with a new property -
// append <K, V> to an object T, if
// - K is not a key of T, append [K]: V to T
// - otherwise where K exists in T
// - if T[K] equals to V, do nothing
// - if T[K] is not an array, change the value type T[K] to [T[K], V]
// - given T[K] is an array, we check if T[K] contains V,
// - if so, do nothing,
// - otherwise, change the value type T[K] to [...T[K], V]
type Contains<T extends any[], V> = T extends []
? false
: T extends [infer A, ...infer R]
? A extends V
? true
: Contains<R, V>
: false
type Append<T extends {}, K extends string, V> = K extends keyof T
? {
[TK in keyof T]: TK extends K
? T[TK] extends V
? V
: T[TK] extends any[]
? Contains<T[TK], V> extends true
? T[TK]
: [...T[TK], V]
: [T[TK], V]
: T[TK]
}
: {
[TK in (keyof T) | K]: TK extends keyof T ? T[TK] : V
}
// Then, let's do the parsing
// First to define a type - a single character acceptable to be keys or values
type Word = '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'|'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
// The type implementation with state machine
// - T is the input string
// - R is the parsed result
// - S is the state, 0 means we are waiting for keys, 1 for values
// - K is the scanned key when S is 1
// - C is the cache saving the scanned characters
type ParseQueryString<T extends string, R extends {} = {}, S extends 0|1 = 0, K extends string = '', C extends string = ''> =
T extends ''
// we reaches to the end of the input string
? S extends 0
// we are scanning keys, if C is non-empty, we need to append <C, true> to R
? C extends ''
? R
: Append<R, C, true>
// S is 1, which means we are scanning a value, append <K, C> to R
// actually here we can further check if C is empty, if so, maybe we can take it as a syntax error
: Append<R, K, C>
: T extends `${infer A extends Word}${infer TR}`
// we come to a single character, add the scanned character into C, and move forward
? ParseQueryString<TR, R, S, K, `${C}${A}`>
: T extends `${infer _ extends '='}${infer TR}`
// we meet a '=' symbol, if S is 1 where we are scanning values, the input string contains syntax errors
// if S is 0, we stop scanning keys and continue scanning values, where we make K = C, S = 1, and C = ''
? S extends 1
? never
: ParseQueryString<TR, R, 1, C, ''>
: T extends `${infer _ extends '&'}${infer TR}`
// we come to the symbol '&', if we are scanning values, we put <K, C> to R, and start scanning keys
// if we are scanning keys, where C is the character we scanned so far for the current key,
// we put <C, true> to R, and restart scanning keys
? S extends 0
? ParseQueryString<TR, Append<R, C, true>, 0, '', ''>
: ParseQueryString<TR, Append<R, K, C>, 0, '', ''>
: never
Solution by lzl1918 #29499
// your answers
//here you can add func to construct tuple to clean code
type GetObj<T extends string, P> = T extends `${infer C}=${infer D}` ? Omit<{
[X in C]: X extends keyof P ? P[X] extends any[] ? D extends P[X][number] ? P[X] : [...P[X], D] : D extends P[X] ? D : [P[X], D] : D
} & Omit<P, C>, never> : Omit<{
[X in T]: X extends keyof P ? P[X] extends any[] ? true extends P[X][number] ? P[X] : [...P[X], true] : true extends P[X] ? true : [P[X], true] : true
} & Omit<P, T>, never>
// {[X in T]:true}
type ParseQueryString<T extends string, R = {}> = T extends `${infer A}&${infer B}` ? ParseQueryString<B, GetObj<A, R>> : T extends '' ? R : GetObj<T, R>
let a: ParseQueryString<'k1&k1'>
Solution by manyuemeiquqi #29474
// 你的答案
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>>>
Solution by MahoushoujoMadoka #29423
type Includes<T extends readonly any[], U> = {
[P in T[number]]: true
}[U] extends true ? true : false;
type HasPairs<String> = String extends `${infer One}&${infer Second}` ? true : false
type GetPairObject<Query extends string> = Query extends `${infer Key}=${infer Value}`
? Record<Key, Value>
: Query extends ''
? {}
: Record<Query, true>
type NewValue<Prev, New> = New extends Prev
? Prev
: Prev extends unknown[]
? Includes<Prev, New> extends true
? Prev
: [...Prev, New]
: [Prev, New]
type AddPair<
Obj extends Record<string, unknown>,
Key extends PropertyKey,
Value,
> = {
[K in keyof Obj | Key]: K extends Key
? K extends keyof Obj
? NewValue<Obj[K], Value>
: Value
: K extends keyof Obj ? Obj[K] : never
}
type ParseQueryString<String extends string, Result extends Record<string, unknown> = {}> = HasPairs<String> extends false
? AddPair<Result, keyof GetPairObject<String>, GetPairObject<String>[keyof GetPairObject<String>]>
: String extends `${infer One}&${infer Second}`
? ParseQueryString<Second, AddPair<Result, keyof GetPairObject<One>, GetPairObject<One>[keyof GetPairObject<One>]>>
: never
Solution by aybykovskii #28837
type ParseItem<S extends string> = S extends `${infer K}=${infer V}`? Record<K,V>: Record<S,true>
type UniqueMerge<T extends any[],U extends any[],> =
U extends [infer F,...infer R]?
F extends T[number]? UniqueMerge<T,R>:UniqueMerge<[...T,F],R>
:T
type Merge<A,B> =
A extends any[]?
B extends any[]?
UniqueMerge<A,B>
:UniqueMerge<A,[B]>
:
B extends any[]?
UniqueMerge<[A],B>
: A extends B? A:[A,B]
type Combine<
T,
U,
> =
{
[K in (keyof T) | (keyof U)]:
K extends keyof T?
K extends keyof U?
Merge<T[K],U[K]>
: T[K]
:
K extends keyof U?
U[K]:never
}
type ParseQueryString<S extends string,R = {}> =
S extends `${infer I}&${infer Z}`?
ParseQueryString<Z, Combine<R,ParseItem<I> > >
:S extends ''?
R:
Combine<R,ParseItem<S> >
Solution by jiangshanmeta #27913
type ResType = Record<string, Array<string | boolean>>
type Merge<A, B> = {
[K in keyof (A & B)]:
K extends keyof A
? A[K]
: K extends keyof B
? B[K]
: never
}
type ParseQueryTerm<T extends string, Res extends ResType> =
T extends `${infer K}=${infer V}`
? K extends keyof Res
? Merge<Omit<Res, K>, { [key in K]: [...Res[K], V] }>
: Merge<Res, { [key in K]: [V] }>
: T extends ""
? {}
: T extends keyof Res
? Merge<Omit<Res, T>, { [key in T]: [...Res[T], true] }>
: Merge<Res, { [key in T]: [true] }>
type ParseQueryStringHelper<S extends string, Res extends ResType = {}> =
S extends `${infer L}&${infer R}`
? ParseQueryStringHelper<R, ParseQueryTerm<L, Res>>
: ParseQueryTerm<S, Res>
type RemoveDupes<A extends any[], Seen extends any[] = []> =
A extends [infer L, ...infer R]
? L extends Seen[number]
? RemoveDupes<R, Seen>
: RemoveDupes<R, [...Seen, L]>
: Seen
type ParseQueryString<S extends string> =
ParseQueryStringHelper<S> extends infer Res extends ResType
? {
[K in keyof Res]:
RemoveDupes<Res[K]> extends [infer X]
? X
: RemoveDupes<Res[K]>
}
: {}
Solution by RuyiLi #27902
type Normalize<T> = {
[Key in keyof T]: T[Key];
} & {};
type ParsedQueryType = Record<string, QueryValueType | QueryValueType[]>;
type QueryValueType = string | true;
type ParseQueryString<Q extends string> = Q extends '' ? {} : Normalize<ParseAllQueries<Q, {}>>;
type ParseAllQueries<
Q extends string,
Acc extends ParsedQueryType = {},
> = Q extends `${infer QFirst}&${infer QRest}`
? ParseAllQueries<QRest, ParseSingleQuery<QFirst, Acc>>
: ParseSingleQuery<Q, Acc>;
type ParseSingleQuery<
Q extends string,
Acc extends ParsedQueryType,
> = Q extends `${infer QKey}=${infer QValue}`
? AssignQueryValue<Acc, QKey, QValue>
: AssignQueryValue<Acc, Q, true>;
type AssignQueryValue<
Acc extends ParsedQueryType,
QKey extends string,
QValue extends QueryValueType,
> = QKey extends keyof Acc
? Omit<Acc, QKey> & {
[Key in QKey]: Acc[Key] extends unknown[]
? QValue extends Acc[Key][number]
? Acc[Key]
: [...Acc[Key], QValue]
: QValue extends Acc[Key]
? Acc[Key]
: [Acc[Key], QValue];
}
: Acc & { [Key in QKey]: QValue };
Solution by BOCbMOU #27613
type As<T, V> = T extends V ? T : never;
type AddParamsToArray<A extends unknown[], value> = Includes<
A,
value
> extends true
? A
: [...A, value];
type Includes<A extends unknown[], value> = A extends [
infer First,
...infer Rest
]
? First extends value
? true
: Includes<Rest, value>
: false;
type Merge<Obj1 extends object, Obj2 extends object> = {
[K in keyof Obj1 | keyof Obj2]: K extends keyof Obj2
? K extends keyof Obj1
? Obj1[K] extends unknown[]
? AddParamsToArray<Obj1[K], Obj2[K]>
: Obj1[K] extends Obj2[K]
? Obj2[K]
: [Obj1[K], Obj2[K]]
: Obj2[K]
: K extends keyof Obj1
? Obj1[K]
: never;
};
/**
* Transform string params to array: "k1&k2=v2" -> ['k1', 'k2=v2']
*/
type TransformParamsStringIntoArray<S extends string> =
S extends `${infer First}&${infer Rest}`
? [First, ...TransformParamsStringIntoArray<Rest>]
: [S];
type FilterEmptyString<A extends unknown[]> = A extends [
infer First,
...infer Rest
]
? First extends ""
? [...FilterEmptyString<Rest>]
: [First, ...FilterEmptyString<Rest>]
: A;
/**
* Transform string params to object: ['k1', 'k2=v2'] -> [{k1: true}, {k2: v2}]
*/
type TransformStringParamsToObject<A extends string[]> = A extends [
infer First,
...infer Rest
]
? First extends `${infer Key}=${infer Value}`
? [
{ [K in Key]: Value },
...TransformStringParamsToObject<As<Rest, string[]>>
]
: [
{ [K in As<First, string>]: true },
...TransformStringParamsToObject<As<Rest, string[]>>
]
: A;
/**
* Merge list of object params: [{k1: true}, {k2: v2}] => {k1: true, k2: v2}
*/
type MergeObjectsParams<A extends object[]> = A extends [
infer First,
infer Second,
...infer Rest
]
? MergeObjectsParams<
[Merge<As<First, object>, As<Second, object>>, ...As<Rest, object[]>]
>
: A extends [infer First]
? First
: {};
// ============= Your Code Here =============
type ParseQueryString<S extends string> = MergeObjectsParams<
TransformStringParamsToObject<
FilterEmptyString<TransformParamsStringIntoArray<S>>
>
>;
Solution by Matth10 #27209
type AndKey<S, P = never> = S extends `${infer A}&${infer B}` ? P | A | AndKey<B, A> : S
type EquKey<S> = S extends `${infer A}=${infer _}` ? A : S
// keys => xx | xx | ...
type Key<S> = EquKey<AndKey<S>> extends '' ? never : EquKey<AndKey<S>>
// value => [xx, xx, xx ...]
type AndKey1<S, P extends any[] = []> = S extends `${infer A}&${infer B}` ? [...P, ...AndKey1<B, [A]>] : [...P, S]
type KY<S extends any[], K extends string, A extends any[] = []> = S extends [infer R, ...infer X]
? [...A, ...KY<X, K, EquVal1<R, K>>]
: A
type EquVal1<S, K extends string> = S extends `${K}=${infer B}` ? [B] : S extends K ? [true] : []
// [1,1,1,2,3] => [1,2,3]
type Repeat<S extends any[], L extends S[number] = S[number]> = S extends [infer R, ...infer X]
? (R extends L ? [R, ...Repeat<X, Exclude<L, R>>] : [])
: []
// length string or arr
type SOA<S extends any[]> = S['length'] extends 1 ? S[0] : S
type ParseQueryString<S extends string> = {
[K in Key<S>]: SOA<Repeat<KY<AndKey1<S>, K>>>
}
Solution by Acoooooooer #26717
type MergeKV<O1, O2> = Omit<
{
[K in Exclude<keyof O1 | keyof O2, keyof O1 & keyof O2>]: K extends keyof O1
? O1[K]
: K extends keyof O2
? O2[K]
: never;
} & {
[K in keyof O1 & keyof O2]: O1[K] extends unknown[]
? O2[K] extends O1[K][number]
? O1[K]
: [...O1[K], O2[K]]
: O2[K] extends O1[K]
? O1[K]
: [O1[K], O2[K]];
},
never
>;
type ParseKV<S extends string, Res> = S extends ''
? {}
: S extends `${infer K}=${infer V}`
? MergeKV<Res, Record<K, V>>
: MergeKV<Res, Record<S, true>>;
type ParseQueryString<
S extends string,
Res extends object = {}
> = S extends `${infer L}&${infer R}`
? ParseQueryString<R, ParseKV<L, Res>>
: ParseKV<S, Res>;
Solution by JohnLi1999 #26432
https://www.youtube.com/watch?v=UxzXFkgL6dQ&ab_channel=AceCode :movie_camera:
type IsInArray<Arr extends unknown[], Value extends string | boolean> =
Arr extends [infer First, ...infer Rest] ?
First extends Value ?
true:
IsInArray<Rest, Value>:
false
type AddToObject<Obj extends Record<string,string | boolean | unknown[]>, Key extends string, Value extends string | boolean = true> = {
[prop in (keyof Obj) | Key]:
prop extends Key ?
prop extends keyof Obj ?
Obj[prop] extends unknown[] ?
IsInArray<Obj[prop], Value> extends true?
Obj[prop]:
[...Obj[prop],Value]:
Obj[prop] extends Value ?
Value :
[Obj[prop],Value]:
Value:
Obj[prop]
}
// type CheckAddToObject = AddToObject<{k1:["v1","v2"], k2: "v2"}, "k1", "v3">
type casesAddToObject = [
Expect<Equal<AddToObject<{},"k1">, { k1: true }>>,
Expect<Equal<AddToObject<{k2: "v2"},"k1", true>, { k2: 'v2'; k1: true }>>,
Expect<Equal<AddToObject<{k2: true},"k1", "v1">, { k2: true; k1: "v1" }>>,
Expect<Equal<AddToObject<{},"k1", "v1">, { k1: 'v1'; }>>,
Expect<Equal<AddToObject<{k2: "v2"},"k1", "v1">, { k2: 'v2'; k1: 'v1' }>>,
Expect<Equal<AddToObject<{k1:"v1"},"k1", "v2">, { k1: ['v1','v2']; }>>,
Expect<Equal<AddToObject<{k1:"v2"},"k1", "v2">, { k1: 'v2'; }>>,
Expect<Equal<AddToObject<{k1:["v2","v1"]},"k1", "v2">, { k1: ['v2',"v1"]; }>>,
Expect<Equal<AddToObject<{k1:"v2"},"k1", true>, { k1: ['v2',true]; }>>,
Expect<Equal<AddToObject<{k1:true},"k1", "v2">, { k1: [true,'v2']; }>>,
Expect<Equal<AddToObject<{k1:true},"k1">, { k1: true; }>>,
Expect<Equal<AddToObject<{k1:["v1","v2"], k2: "v2"}, "k1", "v3">, { k1: ["v1","v2","v3"]; k2: "v2" }>>,
Expect<Equal<AddToObject< { k1: ["v1","v2","v3"]; k2: "v2" }, "k1", "v2">, { k1: ["v1","v2","v3"]; k2: "v2" }>>,
]
type SingleQueryParam<A extends string> =
A extends `${infer key}=${infer value}`?
[key, value] :
[A, true]
type casesSingleQueryParam = [
Expect<Equal<SingleQueryParam<"k1">, ["k1",true]>>,
Expect<Equal<SingleQueryParam<"k2=v2">, ["k2", "v2"]>>,
Expect<Equal<SingleQueryParam<"=v1">, ["","v1"]>>,
Expect<Equal<SingleQueryParam<"v1=">, ["v1", ""]>>,
]
type ParseQueryString<A extends string, Result extends Record<string,string | boolean | unknown[]> = {}> =
A extends '' ?
Result :
A extends `${infer keyValuePair}&${infer Rest}` ?
ParseQueryString<Rest, AddToObject<Result, SingleQueryParam<keyValuePair>[0], SingleQueryParam<keyValuePair>[1]>>:
AddToObject<Result, SingleQueryParam<A>[0], SingleQueryParam<A>[1]>
Solution by AceCodePt #25888
type ExtractKey<T extends string> = T extends `${infer Key}=${string}` ? Key : T
type ExtractValue<T extends string> = T extends `${string}=${infer Value}` ? Value : true
type KVPair<T extends string> = [key: ExtractKey<T>, value: ExtractValue<T>]
type ExtractPairs<T extends string> = T extends `${infer Parameter}&${infer Rest}`
? [...ExtractPairs<Parameter> , ...ExtractPairs<Rest>]
: [KVPair<T>]
type SingleValueOrTuple<Previous, Current> = Current extends Previous
? Current // If they are the same, we don't bother.
: Previous extends any[] // Otherwise, we need to expand / create the tuple.
? Current extends Previous[number]
? Previous // If Current is already in the tuple, we simply return the tuple.
: [...Previous, Current] // Otherwise, we expand the tuple.
: [Previous, Current] // Otherwise, we create a tuple.
type FindValues<T, K extends KVPair<any>[1], Previous = unknown> = T extends [infer First, ...infer Rest]
? First extends KVPair<any>
? First[0] extends K
? FindValues<Rest, K, SingleValueOrTuple<Previous, First[1]>>
: FindValues<Rest, K, Previous>
: FindValues<Rest, K, Previous>
: Previous
type AssembleObject<T extends KVPair<any>[]> = { [k in T[number][0]]: FindValues<T, k> }
type ParseQueryString<T extends string> = T extends ''
? {}
: AssembleObject<ExtractPairs<T>>
Solution by sebwas #25447
/**在集中 */
type BelongTo<T extends any[] | any, U> = T extends (infer P)[] ? (U extends P ? true : false) : (U extends T ? true : false);
/**合并键值对类型 */
type CombineKV<T extends object, U extends object> = { [P in (keyof T) | (keyof U)]:
P extends keyof U ?
(P extends keyof T ?
(BelongTo<T[P], U[P]> extends true ?
T[P] : //键和类型都相同,忽略
(T[P] extends any[] ? [...T[P], U[P]] : [T[P], U[P]]) //键相同,类型不同,判断数组Push
) :
U[P]) : //键不同,新增
(P extends keyof T ? T[P] : never) //键不同,新增
};
/**解析单个 */
type Parse<T extends string> = T extends `${infer K}=${infer V}` ? { [P in K]: V } : { [P in T]: true };
type ParseQueryString<T extends string, _Result extends object = {}> = T extends `` ? _Result : T extends `${infer F}&${infer R}` ? ParseQueryString<R, CombineKV<_Result, Parse<F>>> : CombineKV<_Result, Parse<T>>;
Solution by E-uler #25427
// your answers
type ParseQueryString<S extends string, Res extends Record<string, any> ={} > =
S extends `${infer first}&${infer second}`
? ParseQueryString<second, CheckSameKey<Res, ParseSimpleString<first> >>
: CheckSameKey<Res, ParseSimpleString<S> >
type ParseSimpleString<S extends string> = S extends `${infer first}=${infer second}`
? { [k in first]: second }
: S extends ''
? {}
: { [k in S]: true }
type t = ParseSimpleString<'a=3'>
type CheckSameKey<A, B> =
keyof B extends keyof A ? AddTuple<A, B> : CombineRecord<A, B>
type AddTuple<A, B> = {
[K in keyof A]: K extends keyof B
? B[K] extends A[K]
? A[K]
: A[K] extends any[]
? [...A[K], B[K]]
: [A[K], B[K]]
: A[K]
}
type CombineRecord<A, B> = {
[T in (keyof A | keyof B)]: T extends keyof A
? A[T]
: T extends keyof B
? B[T]
: never
}
Solution by GDCzhou #24673
type ValueType = string | true | (string | true)[]
type GetValue<A, B> =
A extends ValueType
? B extends A
? A
: A extends (string | true)[]
? B extends A[number]
? A
: [...A, B]
: [A, B]
: B
type Copy<T> = {
[K in keyof T]: T[K]
}
type ParseQueryString<
S extends string,
R extends Record<string, ValueType> = {},
> =
S extends ''
? R
: S extends `${infer A}&${infer Rest}`
? ParseQueryString<Rest, ParseQueryString<A, R>>
: S extends `${infer K}=${infer V}`
? Copy<Omit<R, K> & { [Key in K]: GetValue<R[K], V> }>
: Copy<Omit<R, S> & { [Key in S]: GetValue<R[S], true> }>
Solution by drylint #23308
type Tokenize<T extends string> =
T extends `${infer L}&${infer R}`
? [L, ...Tokenize<R>]
: T extends ''
? []
: [T]
type ParseValue<T extends string[]> =
T extends [infer F, ...infer Rest extends string[]]
? F extends `${infer K}=${infer V}`
? [[K, V], ...ParseValue<Rest>]
: [[F, true], ...ParseValue<Rest>]
: []
type Replace<O, K, V> =
{[P in Exclude<keyof O, K>]: O[P]}
& {[P in K & string]: V}
type ReduceKV<T extends [string, unknown][], _ACC extends Record<string, unknown[]> = {}> =
T extends [[infer K extends string, infer V], ...infer Rest extends [string, unknown][]]
? K extends keyof _ACC
? V extends _ACC[K][number] // 避免添加重复的类型。直接在这里处理该逻辑,既能利用已经获取到的 V,又能直接从 _ACC[K] 中并出现有的所有类型,方便
? ReduceKV<Rest, _ACC>
: ReduceKV<Rest, Replace<_ACC, K, [..._ACC[K], V]>>
: ReduceKV<Rest, _ACC & {[P in K]: [V]}>
: _ACC
type ExpandSingleValue<T> = {
[P in keyof T]: T[P] extends [infer V] ? V : T[P]
}
type ParseQueryString<S extends string> = ExpandSingleValue<ReduceKV<ParseValue<Tokenize<S>>>>
Solution by zhaoyao91 #22106
// your answers
type ParseQueryString<
S extends string,
T extends { [_ in any]: any } = {},
K extends keyof T = keyof T
> =
S extends ''
? T
: (S extends `${infer A}&${infer B}`
? [A, B]
: [S, '']
) extends [infer A extends string, infer B extends string]
? ((A extends `${infer AA extends string}=${infer BB extends string}`
? [AA, BB]
: [A, true]
// xx=yy&zzzz >>> AAA=xx,BBB=YY. xx&yyy >>> AAA=xx,BBB=true
) extends [infer AAA extends string, infer BBB]
? (ParseQueryString<B,
//以下为把新内容放入 T 里面,需要去重,多个值要 套[] 等等
{ [P in (K | AAA)]: // keys
(P extends AAA
? (P extends K
? (T[P] extends any[]
? BBB extends T[P][number] ? T[P] : [...T[P], BBB]
: BBB extends T[P] ? T[P] : [T[P], BBB]
)
: BBB
)
: T[P]
)
}>
)
: 'useless'
)
: 'useless';
Solution by goddnsgit #22086