1

I am new to Typescript and I wrote a very rudimentary implementation of JSON stringify. The code works as expected, but my types are a bit messed up on the part where I recursively stringify nested arrays. Any help is appreciated :D

Here is a link to the TS playground and there it shows all the errors I get. PLAYGROUND LINK

type ValidJSON = ValidJSONObject | string | number | boolean | JsonArray

interface JsonArray extends Array<string | number | boolean | Date | ValidJSONObject | JsonArray> { }

interface ValidJSONObject {
    [x: string]: string | number | boolean | Date | JsonArray
}

export const stringify = (input: ValidJSON) :string => {
   if (input === null)
    return 'null'
  else if (input.constructor === String)
    return '"' + input.replace(/"|\n/g, (x:string) =>  x === '"' ? '\\"' :'\\n') + '"'
  else if (input.constructor === Number)
    return String(input)
  else if (input.constructor === Boolean)
    return input ? 'true' : 'false'
  else if (input.constructor === Array)
    return '[' + input.reduce((acc, v) => {
      if (v === undefined)
        return [...acc, 'null']
      else
        return [...acc, stringify(v)]
    }, []).join(',') + ']'
  else if (input.constructor === Object)
    return '{' + Object.keys(input).reduce((acc, k) => {
      if (input[k] === undefined)
        return acc
      else
        return [...acc, stringify(k) + ':' + stringify(input[k])]
    }, []).join(',') + '}'
  else
    return '{}'
};

1 Answer 1

2

The reduce method need type argument if the accumulator type cannot be inferenced.

For example. The accumulator of following code doesn't tell what type of the array is.

[1,2,3].reduce((acc, cur) => ([...acc, cur+'']), [])

So you need to pass type argument like follows.

[1,2,3].reduce<string[]>((acc, cur) => ([...acc, cur+'']), [])

I also fixed the all of errors. The changes as follows.

  1. Added Date type to the ValidJSON type definition.
  2. Added string[] to type argument of reduce method.
  3. Cast the input as (input as ValidJSONObject)

The generated javascript code is the same of yours.
I hope this will help!

playground

type ValidJSON = ValidJSONObject | string | number | boolean | Date | JsonArray

interface JsonArray extends Array<string | number | boolean | Date | ValidJSONObject | JsonArray> { }

interface ValidJSONObject {
    [x: string]: string | number | boolean | Date | JsonArray
}

export const stringify = (input: ValidJSON) :string => {
   if (input === null)
    return 'null'
  else if (input.constructor === String)
    return '"' + input.replace(/"|\n/g, (x:string) =>  x === '"' ? '\\"' :'\\n') + '"'
  else if (input.constructor === Number)
    return String(input)
  else if (input.constructor === Boolean)
    return input ? 'true' : 'false'
  else if (input.constructor === Array)
    return '[' + input.reduce<string[]>((acc, v) => {
      if (v === undefined)
        return [...acc, 'null']
      else
        return [...acc, stringify(v)]
    }, []).join(',') + ']'
  else if (input.constructor === Object)
    return '{' + Object.keys(input).reduce<string[]>((acc, k: keyof ValidJSONObject) => {
      if ((input as ValidJSONObject)[k] === undefined)
        return acc
      else
        return [...acc, stringify(k) + ':' + stringify((input as ValidJSONObject)[k])]
    }, []).join(',') + '}'
  else
    return '{}'
};
Sign up to request clarification or add additional context in comments.

3 Comments

Why is it not just interface JsonArray extends Array<ValidJSON>?
And also, why can properties of object not be other objects?
looks like I was on the right path with type assertions but I was not sure how to use them yet. Thank you so much for this!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.