-
Notifications
You must be signed in to change notification settings - Fork 103
Description
I'm looking for a type-safe way to represent "undefined" (similar to TypeScript) values in input types. I define "undefined" values as the lack of provided value (by the client) for a specific class field (PHP calls these properties) in an input type (class).
I've read some issues on this matter already, such as #210. But none of the proposed solutions is good enough.
Desired behavior
I want GraphQLite to resolve/map/set the value of an input class field (or constructor parameter) to a custom type made by me when no value was provided for that field by the client.
Type-safe
With "type-safe" I mean a solution that the type system of PHP communicates to and guarantees the developer of:
- The type of value that is saved in a class field. That is, the PHP type annotation communicates that a field might not have been provided.
- The value that will be returned when the getter is called. That is, no other type than the declared types is returned.
- That no obscure or unintended behavior will happen. That is, no TypeError should be thrown because a developer simply called a getter.
No hackiness
I don't want to use isset()
or other hacky things to first check if a field is initialized.
Nullability
The type of the field in the input class can still be nullable and undefined-able (not a word but you understand what I mean).
In TypeScript, this would be expressed as string|null|undefined
.
For completeness' sake, it would be nice to have EDIT: Never mind. This library can't map a field with only null|undefined
as a possibility (even though it sounds weird).null
as the type, so this shouldn't be possible either.
"Undefined" type
After some research, I have determined that it would be best to use a PHP enum as a custom type to represent undefined values (I can elaborate on this conclusion if needed).
enum Undefined
{
case UNDEFINED;
}
Example of the desired outcome
An example of an input type:
#[Input]
readonly class MayHaveUndefinedValueObject
{
#[Field]
private string $name;
#[Field]
private string|null|Undefined $description;
#[Field]
private bool|Undefined $important;
/**
* @param string $name
* @param string|null|Undefined $description
* @param bool|Undefined $important
*/
public function __construct(
string $name,
string|null|Undefined $description,
bool|Undefined $important
) {
$this->name = $name;
$this->description = $description;
$this->important = $important;
}
public function getName(): string
{
return $this->name;
}
public function getDescription(): string|null|Undefined
{
return $this->description;
}
public function getImportant(): bool|Undefined
{
return $this->important;
}
}
As you see, it is also desired that this works with private fields that only have getters and no setters. It would also be nice to have the ability to use readonly
classes.
Notice how the return type of the getter clearly communicates that the field's value might not have been set by the client. In my project, currently, calling the getter might throw TypeError
which the developer might not expect with no clear knowledge of how this class is being used in different places.
I would like a way for GraphQLite to automatically set the value of constructor parameters, that accept Undefined
, to Undefined::UNDEFINED
. From what I know, currently, GraphQLite will set the value to null
if the parameter accepts it and no value is provided.
Already looked into
- Root type mappers.
a. I tried to implement this but couldn't figure out how to map from something that doesn't exist (because no value is provided). This was a couple of months ago and I don't have the code anymore. - (Class) Type mappers.
a. Read the documentation but I don't think this can work. - Parameter middleware.
a. Haven't tried it and I'm not sure that this could work.
🥅 What I wish to achieve with this issue
I would like to get answers to these questions:
- Is it currently possible to make GraphQLite set the value of a parameter or field (that has no provided value) to a custom type? And how?
- If question 1 is not possible, could you at least provide me with hints as to which parts of the GraphQLite code base need to be changed to make this possible? And will the maintainers be open for a pull request for this feature?
- Are there any workarounds or existing solutions that you might recommend for my goals?
Extra requirements
I don't want to use #[Input(name: 'MayHaveUndefinedValueObjectUpdateInput', update: true)]
because:
- It is not type-safe.
- The getters don't communicate that the field might be undefined.
- The getters might unexpectedly throw a
TypeError
. - I don't want to use hacky things like
isset()
.