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 JSON.stringify return type (#18879), reduce usage of any in JSON.parse #50242

Closed
wants to merge 10 commits into from
Closed
28 changes: 21 additions & 7 deletions src/lib/es5.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1113,21 +1113,35 @@ interface JSON {
* @param reviver A function that transforms the results. This function is called for each member of the object.
* If a member contains nested objects, the nested objects are transformed before the parent object is.
*/
parse(text: string, reviver?: (this: any, key: string, value: any) => any): any;
parse(text: string, reviver?: (this: unknown, key: string, value: unknown) => unknown): any;
/**
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
* @param value A JavaScript value, usually an object or array, to be converted.
* Converts a JSON value to a JavaScript Object Notation (JSON) string.
* @param value A JSON value, usually an object or array, to be converted.
* @param replacer A function that transforms the results.
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
*/
stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string;
stringify(value: boolean | number | string | object, replacer?: (this: unknown, key: string, value: unknown) => unknown, space?: string | number): string;
/**
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
* @param value A JavaScript value, usually an object or array, to be converted.
* Converts a JSON value to a JavaScript Object Notation (JSON) string.
* @param value A JSON value, usually an object or array, to be converted.
* @param replacer An array of strings and numbers that acts as an approved list for selecting the object properties that will be stringified.
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
*/
stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
stringify(value: boolean | number | string | object, replacer?: (number | string)[] | null, space?: string | number): string;
/**
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string or undefined.
* @param value A JavaScript value to be converted, which could potentially be a non-JSON value.
* @param replacer A function that transforms the results.
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
*/
stringify(value: unknown, replacer?: (this: unknown, key: string, value: unknown) => unknown, space?: string | number): string | undefined;
Copy link
Member

Choose a reason for hiding this comment

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

this: unknown requires this to be unknown or a supertype of unknown if it's specified. That doesn't sound right to me. What types are supposed to allowed and disallowed for this here?

Copy link
Author

Choose a reason for hiding this comment

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

this is always set to the value being replaced, which doesn't have any distinct type. It could be a symbol or function or some other value added to the standard in the distant future, which is why I was conservative.

When you say "supertype of unknown"... I thought that unknown was the maximal type?

/**
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string or undefined.
* @param value A JavaScript value to be converted, which could potentially be a non-JSON value.
* @param replacer An array of strings and numbers that acts as an approved list for selecting the object properties that will be stringified.
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
*/
stringify(value: unknown, replacer?: (number | string)[] | null, space?: string | number): string | undefined;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/controlFlowIfStatement.types
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ function c<T>(data: string | T): T {

return JSON.parse(data);
>JSON.parse(data) : any
>JSON.parse : (text: string, reviver?: (this: any, key: string, value: any) => any) => any
>JSON.parse : (text: string, reviver?: (this: unknown, key: string, value: unknown) => unknown) => any
>JSON : JSON
>parse : (text: string, reviver?: (this: any, key: string, value: any) => any) => any
>parse : (text: string, reviver?: (this: unknown, key: string, value: unknown) => unknown) => any
>data : string | (T & string)
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,9 @@ export class HTMLtoJSX {
>whitespace : Symbol(whitespace, Decl(controlFlowPropertyDeclarations.ts, 121, 50))

return '{' + JSON.stringify(whitespace) + '}';
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>whitespace : Symbol(whitespace, Decl(controlFlowPropertyDeclarations.ts, 121, 50))

});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,9 @@ export class HTMLtoJSX {
>'{' + JSON.stringify(whitespace) : string
>'{' : "{"
>JSON.stringify(whitespace) : string
>JSON.stringify : { (value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (string | number)[], space?: string | number): string; }
>JSON.stringify : { (value: string | number | boolean | object, replacer?: (this: unknown, key: string, value: unknown) => unknown, space?: string | number): string; (value: string | number | boolean | object, replacer?: (string | number)[], space?: string | number): string; (value: unknown, replacer?: (this: unknown, key: string, value: unknown) => unknown, space?: string | number): string; (value: unknown, replacer?: (string | number)[], space?: string | number): string; }
>JSON : JSON
>stringify : { (value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (string | number)[], space?: string | number): string; }
>stringify : { (value: string | number | boolean | object, replacer?: (this: unknown, key: string, value: unknown) => unknown, space?: string | number): string; (value: string | number | boolean | object, replacer?: (string | number)[], space?: string | number): string; (value: unknown, replacer?: (this: unknown, key: string, value: unknown) => unknown, space?: string | number): string; (value: unknown, replacer?: (string | number)[], space?: string | number): string; }
>whitespace : string
>'}' : "}"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ const Child: SFC<Props> = ({

}) => `name: ${name} props: ${JSON.stringify(props)}`;
>name : Symbol(name, Decl(destructuringInitializerContextualTypeFromContext.ts, 15, 13))
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>props : Symbol(props, Decl(destructuringInitializerContextualTypeFromContext.ts, 16, 21))

// Repro from #29189
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ const Child: SFC<Props> = ({
>`name: ${name} props: ${JSON.stringify(props)}` : string
>name : "Apollo" | "Artemis" | "Dionysus" | "Persephone"
>JSON.stringify(props) : string
>JSON.stringify : { (value: any, replacer?: ((this: any, key: string, value: any) => any) | undefined, space?: string | number | undefined): string; (value: any, replacer?: (string | number)[] | null | undefined, space?: string | number | undefined): string; }
>JSON.stringify : { (value: string | number | boolean | object, replacer?: ((this: unknown, key: string, value: unknown) => unknown) | undefined, space?: string | number | undefined): string; (value: string | number | boolean | object, replacer?: (string | number)[] | null | undefined, space?: string | number | undefined): string; (value: unknown, replacer?: ((this: unknown, key: string, value: unknown) => unknown) | undefined, space?: string | number | undefined): string | undefined; (value: unknown, replacer?: (string | number)[] | null | undefined, space?: string | number | undefined): string | undefined; }
>JSON : JSON
>stringify : { (value: any, replacer?: ((this: any, key: string, value: any) => any) | undefined, space?: string | number | undefined): string; (value: any, replacer?: (string | number)[] | null | undefined, space?: string | number | undefined): string; }
>stringify : { (value: string | number | boolean | object, replacer?: ((this: unknown, key: string, value: unknown) => unknown) | undefined, space?: string | number | undefined): string; (value: string | number | boolean | object, replacer?: (string | number)[] | null | undefined, space?: string | number | undefined): string; (value: unknown, replacer?: ((this: unknown, key: string, value: unknown) => unknown) | undefined, space?: string | number | undefined): string | undefined; (value: unknown, replacer?: (string | number)[] | null | undefined, space?: string | number | undefined): string | undefined; }
>props : {}

// Repro from #29189
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration4.ts(
a1(...array2); // Error parameter type is (number|string)[]
~~~~~~
!!! error TS2552: Cannot find name 'array2'. Did you mean 'Array'?
!!! related TS2728 /.ts/lib.es5.d.ts:1492:13: 'Array' is declared here.
!!! related TS2728 /.ts/lib.es5.d.ts:1506:13: 'Array' is declared here.
a5([1, 2, "string", false, true]); // Error, parameter type is [any, any, [[any]]]
~~~~~~~~
!!! error TS2322: Type 'string' is not assignable to type '[[any]]'.
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/destructuringTuple.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ tests/cases/compiler/destructuringTuple.ts(11,60): error TS2769: No overload mat
!!! error TS2769: Overload 2 of 3, '(callbackfn: (previousValue: [], currentValue: number, currentIndex: number, array: number[]) => [], initialValue: []): []', gave the following error.
!!! error TS2769: Type 'never[]' is not assignable to type '[]'.
!!! error TS2769: Target allows only 0 element(s) but source may have more.
!!! related TS6502 /.ts/lib.es5.d.ts:1457:24: The expected type comes from the return type of this signature.
!!! related TS6502 /.ts/lib.es5.d.ts:1463:27: The expected type comes from the return type of this signature.
!!! related TS6502 /.ts/lib.es5.d.ts:1471:24: The expected type comes from the return type of this signature.
!!! related TS6502 /.ts/lib.es5.d.ts:1477:27: The expected type comes from the return type of this signature.
~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 2, '(...items: ConcatArray<never>[]): never[]', gave the following error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ tests/cases/conformance/types/members/duplicateNumericIndexers.ts(25,5): error T
tests/cases/conformance/types/members/duplicateNumericIndexers.ts(29,5): error TS2374: Duplicate index signature for type 'number'.
tests/cases/conformance/types/members/duplicateNumericIndexers.ts(30,5): error TS2374: Duplicate index signature for type 'number'.
lib.es5.d.ts(523,5): error TS2374: Duplicate index signature for type 'number'.
lib.es5.d.ts(1478,5): error TS2374: Duplicate index signature for type 'number'.
lib.es5.d.ts(1492,5): error TS2374: Duplicate index signature for type 'number'.


==== tests/cases/conformance/types/members/duplicateNumericIndexers.ts (12 errors) ====
Expand Down
20 changes: 10 additions & 10 deletions tests/baselines/reference/json.stringify.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,36 @@ var value = null;
>value : Symbol(value, Decl(json.stringify.ts, 0, 3))

JSON.stringify(value, undefined, 2);
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>value : Symbol(value, Decl(json.stringify.ts, 0, 3))
>undefined : Symbol(undefined)

JSON.stringify(value, null, 2);
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>value : Symbol(value, Decl(json.stringify.ts, 0, 3))

JSON.stringify(value, ["a", 1], 2);
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>value : Symbol(value, Decl(json.stringify.ts, 0, 3))

JSON.stringify(value, (k) => undefined, 2);
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>value : Symbol(value, Decl(json.stringify.ts, 0, 3))
>k : Symbol(k, Decl(json.stringify.ts, 4, 23))
>undefined : Symbol(undefined)

JSON.stringify(value, undefined, 2);
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>value : Symbol(value, Decl(json.stringify.ts, 0, 3))
>undefined : Symbol(undefined)

Loading