Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix return type for JSON.stringify(any) -> string #38574

Closed
wants to merge 9 commits into from

Conversation

jupiter
Copy link

@jupiter jupiter commented May 14, 2020

Fixes #18879

Problem
Current type definitions falsely assert that JSON.stringify(...) always returns a string. JSON.stringify(...) actually returns undefined whenever the input value is undefined, a Function, or a Symbol (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). It also returns undefined when a provided replacer function returns any of these types for the root value.

The fix
We provide overloads to ensure that when valid JSON values are provided, the return type is string. When an invalid value is provided, the return type is undefined as per runtime behaviour. When the input value type is any or when a replacer function is passed the return type is string | undefined.

@typescript-bot
Copy link
Collaborator

It looks like you've sent a pull request to update our 'lib' files. These files aren't meant to be edited by hand, as they consist of last-known good states of the compiler and are generated from 'src'. Unless this is necessary, consider closing the pull request and sending a separate PR to update 'src'.

@typescript-bot typescript-bot added the lib update PR modifies files in the `lib` folder label May 14, 2020
@msftclas
Copy link

msftclas commented May 14, 2020

CLA assistant check
All CLA requirements met.

@jupiter jupiter changed the title Fix 18879 Fix return type for JSON.stringify() May 14, 2020
@jupiter jupiter marked this pull request as ready for review May 14, 2020 17:14
src/lib/es5.d.ts Show resolved Hide resolved
@RyanCavanaugh
Copy link
Member

@typescript-bot test this
@typescript-bot user test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented May 14, 2020

Heya @RyanCavanaugh, I've started to run the parallelized community code test suite on this PR at f26c011. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented May 14, 2020

Heya @RyanCavanaugh, I've started to run the extended test suite on this PR at f26c011. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master.

@jupiter
Copy link
Author

jupiter commented May 14, 2020 via email

@jupiter
Copy link
Author

jupiter commented May 22, 2020

@RyanCavanaugh Can we see how the update does with user tests? I'm thinking that JSON.stringify(any) -> any will do much better. I'm thinking that JSON.stringify(any) -> string was probably a mistake originally due to the function name leading the type. A false type is worse than any.

@wongjiahau
Copy link

Hopefully this can be merged soon, because this issue is bugging me a lot.

@igoradamenko
Copy link

Same here.

I've shot myself in the foot because of JSON.parse(JSON.stringify(variable)), where variable suddenly became undefined and the whole expression has thrown an error.

@nathggns
Copy link

Is this not progressing due to conflicts/an issue with the fix, or has there just not been time to merge it yet? I.e, is the blocker with the author or with maintainers? Trying to evaluate whether to submit a PR myself or not as was just bitten by this bug. (Not trying to spam here – just wondering if I can assist)

@jupiter jupiter changed the title Fix return type for JSON.stringify() Fix return type for JSON.stringify(any) -> string Feb 16, 2021
@reverofevil
Copy link

reverofevil commented Mar 7, 2021

@jupiter Are you sure these types are correct?

I think it doesn't handle at least

JSON.stringify(() => {})
JSON.stringify({toJSON: () => {}})

Update. Found that rbuckton was first to point on this in comments, and why Function overload disappeared.

stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
stringify(
value: any,
replacer: (this: any, key: string, value: any) => number | string | boolean | null | object,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON.stringify({}, () => () => {})

uses this overload and returns string instead of undefined.

): string;
stringify(
value: any,
replacer?: (this: any, key: string, value: any) => any,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think replacer shouldn't be optional here.

stringify(
value: undefined | Symbol,
replacer?: (number | string)[] | null,
space?: any

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why space is any here?

@reverofevil
Copy link

Funny enough, even the best types I could imagine are not enough. Due to some bugfeature in handling of overloaded functions, union of undefined-returning and string-returning argument types is typed incorrectly.

interface Json {
    // two cases cover all types of values available in EcmaScript, except for bigint (intentionally omitted)
    stringify(value: {toJSON: () => undefined | Symbol | Function}, replacer?: (number | string)[] | null, space?: string | number): undefined;
    stringify(value: {toJSON: () => number | string | boolean | null | object}, replacer?: (number | string)[] | null, space?: string | number): string;
    stringify(value: undefined | Symbol | Function, replacer?: (number | string)[] | null, space?: string | number): undefined;
    stringify(value: number | string | boolean | null | object, replacer?: (number | string)[] | null, space?: string | number): string;
    stringify(value: any, replacer: (this: any, key: string, value: any) => number | string | boolean | null | any[] | Record<string, any>, space?: string | number): string;
    stringify(value: any, replacer: (this: any, key: string, value: any) => any, space?: string | number): string | undefined;
}
declare const Json: Json;
declare const x: (() => void) | 1;
const t4 = Json.stringify(x); // string

@mtsoltan
Copy link

Can this, or some other PR that fixes #18879 , get looked at again?

@sandersn
Copy link
Member

Unfortunately, we never finished reviewing this PR. It is pretty old now, so I'm going to close it to reduce the number of open PRs.

@sandersn sandersn closed this May 24, 2022
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Backlog Bug PRs that fix a backlog bug lib update PR modifies files in the `lib` folder
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Type signature for JSON.stringify does not include undefined in the return type