打字稿方法查询字符串到对象

Typescript method querystring to object

我正在制作一种将查询字符串转换为对象的方法。方法很好用,就是打字报错

https://jsfiddle.net/h723zuen/2/

const fromQueryString = <
  T extends {
    [x: string]: string | number | boolean | string[] | number[] | boolean[];
  }
>(
  querystring: string,
) => {
  const out: {
    [x: string]: string | number | boolean | string[] | number[] | boolean[];
  } = {};
  const arr = querystring.split('&');

  arr.forEach((a) => {
    const [key, value] = a.split('=');
    console.log(key, toValue(value), out, key in out);
    if (!!key && !!value) {
      if (key in out) {
        out[key] = Array.isArray(out[key])
          ? out[key].push(toValue(value))
          : [out[key], toValue(value)];
      } else {
        out[key] = toValue(value);
      }
      console.log(out);
    }
  });

  return out as T;
};
function toValue(mix: string) {
  const str = decodeURIComponent(mix);
  if (str === 'false') return false;
  if (str === 'true') return true;
  return +str * 0 === 0 ? +str : str;
}

根据我的理解,Typescript 仍然包含一些有时很难理解的故障。

例如,这与您的案例非常相关:

// (1)
// doesn't work since Typescript can't infer (string | number) as element
type Foo = string[] | number[];
const foo: Foo = [];
foo.push(1); // you would see error here as we push a number element

// (2)
// Works as expected
type Baz = Array<string | number>;
const baz: Baz = [];
baz.push(1);

回到你的案例,我建议你将你的类型转换为案例 (2)。这是您转换后的代码:

// simplify things by group things again
type BaseType = string | number | boolean;
type BaseObj = Record<string, BaseType | BaseType[]>;

const fromQueryString = <T extends BaseObj>(
  querystring: string,
) => {
  const out: BaseObj = {};

  const arr = querystring.split('&');

  arr.forEach((a) => {
    const [key, value] = a.split('=');
    console.log(key, toValue(value), out, key in out);
    if (!!key && !!value) {
      if (key in out) {
        const v = out[key];
        out[key] = Array.isArray(v)
          ? v.push(toValue(value))
          : [v, toValue(value)];
      } else {
        out[key] = toValue(value);
      }
      console.log(out);
    }
  });

  return out as T;
};

function toValue(mix: string) {
  const str = decodeURIComponent(mix);
  if (str === 'false') return false;
  if (str === 'true') return true;
  return +str * 0 === 0 ? +str : str;
}