00151-extreme-query-string-parser

Back

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 };

Playground

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

playground

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

type MergeOBj<O1 extends object, O2 extends object> = {
  [K in keyof O1 | keyof O2]: K extends keyof O1
    ? K extends keyof O2
      ? O1[K] extends Array<any>
        ? O2[K] extends O1[K][number] ? O1[K] : [...O1[K], O2[K]]
        : IsEqual<O1[K], O2[K]> extends true ? O1[K] : [O1[K], O2[K]]
      : O1[K]
    : K extends keyof O2
      ? O2[K]
      : never
}

type MergeObjReduce<A, O extends object = {}> = A extends [
  infer F extends string,
  ...infer R,
]
  ? (F extends `${string}=${string}` ? Split<F, '='> : [F, true]) extends [
      infer Key extends string,
      infer Value,
    ]
      ? MergeObjReduce<R, MergeOBj<O, {
        [K in Key]: Value
      }>>
      : never
  : O

type ParseQueryString<T extends string> = T extends '' ? {} : MergeObjReduce<Split<T, '&'>>

Solution by thanhhtse04588 #22019

type _ToLiteralObject<T> = { [K in keyof T]: T[K] }

type _TupleDedup<A extends unknown[], _Union = never, _Result extends unknown[] = []> =
  A extends [ infer F, ...infer Rest ]
    ? F extends _Union
      ? _TupleDedup<Rest, _Union, _Result>
      : _TupleDedup<Rest, _Union | F, [ ..._Result, F ]>
    : _Result

type _GenerateProp<K extends string, V, O> =
  K extends keyof O
    ? (
      Equal<O[K], V> extends true
        ? Record<K, V>
        : O[K] extends unknown[]
          ? Record<K, _TupleDedup<[ V, ...O[K] ]>>
          : Record<K, _TupleDedup<[ V, O[K] ]>>
    ) & Omit<O, K>
    : Record<K, V> & O


type ParseQueryString<Q extends string> = _ToLiteralObject<
Q extends `${ infer Key }=${ infer Value }&${ infer Rest }`
  ? _GenerateProp<Key, Value, ParseQueryString<Rest>>
  : Q extends `${ infer Key }&${ infer Rest }`
    ? _GenerateProp<Key, true, ParseQueryString<Rest>>
    : Q extends `${ infer Key }=${ infer Value }`
      ? Record<Key, Value>
      : Q extends '' ? {} : Record<Q, true>
>

Solution by lvjiaxuan #21802

type ConvertStrToRecord = str extends ${infer Key}=${infer Value} ? { [K in Key]: Value } : str extends ${infer Key} ? { [K in Key]: true } : {};

type CombineR<A extends Record<string, any>, B extends Record<string, any>> = { [K in keyof A | keyof B]: K extends keyof A ? A[K] : K extends keyof B ? B[K] : never; };

type checkDuplicate< A extends Record<string, any>, B extends Record<string, any>

= keyof B extends keyof A ? AddR<A, B> : CombineR<A, B>;

type AddR<A extends Record<string, any>, B extends Record<string, any>> = { [K in keyof A]: K extends keyof B ? CheckInclude<A[K], B[K]> extends true ? A[K] : A[K] extends any[] ? [...A[K], B[K]] : [A[K], B[K]] : A[K]; };

type CheckInclude<A extends any[], B extends string> = A extends [ infer First, ...infer Rest ] ? First extends B ? true : CheckInclude<Rest, B> : false;

type Parse< Str extends string, Res extends Record<string, any> = {}

= Str extends ${infer One}&${infer Rest} ? Parse<Rest, checkDuplicate<Res, ConvertStrToRecord>> : checkDuplicate<Res, ConvertStrToRecord>;

type res = Parse<"a=1&b&a=3&c&d&e&beyond&beyond=666&a=3">;

Solution by Haiananan #21714

My answer

type ParseWithoutAmpersand<T extends string> =
    T extends `${infer before}=${infer after}`
    ? { [key in before]: after }
    : { [key in T]: true }

type Equal<T, U> =
    (<G>() => G extends T ? true : false) extends (<G>() => G extends U ? true : false)
    ? true : false

//T2 is not array
type UniteValues<V1, V2> =
    V1 extends any[]
    ? (V2 extends V1[number]
        ? V1
        : [...V1, V2])
    : (Equal<V1, V2> extends true
        ? V1
        : [V1, V2])

//T2 do not contain arrays
type Unite<T1 extends object, T2 extends object> = {
    [key in keyof T1 | keyof T2]:
    key extends keyof T1
    ? (key extends keyof T2
        ? UniteValues<T1[key], T2[key]>
        : T1[key])
    : (key extends keyof T2
        ? T2[key]
        : never)
}

type ParseQueryString<Query extends string, Accummulator extends object = {}> =
    Query extends ""
    ? {}
    //finds first ampersand, so `Before` do not have any
    : (Query extends `${infer Before}&${infer After}`
        ? ParseQueryString<After, Unite<Accummulator, ParseWithoutAmpersand<Before>>>
        : Unite<Accummulator, ParseWithoutAmpersand<Query>>)

For now there is no test with adding duplicate value to array

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

Solution by Boobl1k #21566