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

Option for adding undefined to optional union for optional properties. #604

Open
jeremyjacob opened this issue Jun 23, 2024 · 3 comments
Open

Comments

@jeremyjacob
Copy link

In TypeScript, when exactOptionalPropertyTypes is set to true, properties that are optional?: but not do not include | undefined in their union can not be assigned a value of undefined.

For generating code that will be consumed by others, we may not want to force them to turn off exactOptionalPropertyTypes. For that please consider an option to include undefined in generated type unions of non-required properties.

@bcherny
Copy link
Owner

bcherny commented Jun 24, 2024

Could you share an example of a JSON schema and the output it produces today, and how exactly that might cause issues for users?

@jeremyjacob
Copy link
Author

jeremyjacob commented Jun 25, 2024

No problem. Given the schema,

{
	"title": "Example Schema",
	"type": "object",
	"properties": {
		"firstName": {
			"type": "string"
		},
		"age": {
			"type": "integer",
			"minimum": 0
		}
	},
	"additionalProperties": false,
	"required": ["firstName"]
}

the library currently produces the following output:

export interface ExampleSchema {
  firstName: string;
  age?: number;
}

Let's say we have a function,

function testFunction(data: ExampleSchema) { /* ... */ }

Calling that function with age set to undefined raises a TypeScript error when the TS compiler option exactOptionalPropertyTypes is true:

testFunction({
    firstName: "John",
    age: undefined
});

/*
Argument of type '{ firstName: string; age: undefined; }' is not assignable to parameter of type 'ExampleSchema' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
  Types of property 'age' are incompatible.
    Type 'undefined' is not assignable to type 'number'.
*/

The solution in the above is to simply omit the property altogether, but if the value of age is coming from another possibly undefined variable, things get messier:

const age: number | undefined = undefined;
testFunction({
    firstName: "John",
    ...(age ? { age: age } : {})
});

Our code may internally equate the non-existence of an optional type with the existence a value of undefined. This seems to be common behavior in many libraries. If there does need to be a distinction, like with updating a record, using null seems to be the most widely implemented practice.

Thusly I propose a new option in json-schema-to-typescript to address this use case. Possible names include undefinedOptionals and undefinedOptionalProperties.

The following would be the output of the above schema with this flag enabled:

export interface ExampleSchema {
  firstName: string;
  age?: number | undefined;
}
testFunction({
    firstName: "John",
    age: undefined // Ok
});

@bcherny
Copy link
Owner

bcherny commented Jun 25, 2024

Gotcha. That sounds reasonable to me. I think the more explicit undefinedOptionalProperties is a bit nicer. PRs welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants