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

isSpecifiedDirective should not assume Directive type #1837

Merged
merged 1 commit into from May 3, 2019
Merged

isSpecifiedDirective should not assume Directive type #1837

merged 1 commit into from May 3, 2019

Conversation

Cito
Copy link
Member

@Cito Cito commented Apr 29, 2019

isSpecifiedDirective should not just check the name property, but also the type

@IvanGoncharov
Copy link
Member

@Cito Thanks for PR 👍
I just trying to understand what's the problem with the current check.
Idea is to have non-overlapping checks so if you have value and want to enusure it's a directive you would use:

declare function isDirective(
directive: mixed,
): boolean %checks(directive instanceof GraphQLDirective);

Also, note that isSpecifiedDirective take directive of type GraphQLDirective

export function isSpecifiedDirective(
directive: GraphQLDirective,
): boolean %checks {

So if you need to test if some value is standard directive you would do:

if (isDirective(value) && isSpecifiedDirective(value)) {
  // ...
}

What do you think?

@Cito
Copy link
Member Author

Cito commented Apr 30, 2019

I just trying to understand what's the problem with the current check.

The problem was that it was unclear to me that this should be considered a non-overlapping, additional check, and not as a standalone check. To me a standalone check would be more intuitive. I was also looking at these four tests which run the predicate against various garbage. These tests should be removed as superfluous and misleading if the input is supposed to be a directive anyway. Otherwise, a test with {name: 'skip'} as garbage should be added as well.

Just had a look at isSpecifiedScalarType for comparison, which is even more unclear, since it makes an isNamedType check internally. When it's supposed to be a non-overlapping check it should assume that it's a ScalarType already, right? And otherwise, it should check more speficifally for ScalarType, not NamedType.

@IvanGoncharov
Copy link
Member

I was also looking at these four tests which run the predicate against various garbage.

You added them in #1781 😄

Just had a look at isSpecifiedScalarType for comparison, which is even more unclear, since it makes an isNamedType check internally.

isIntrospectionType also has it 😞

export function isIntrospectionType(type: mixed): boolean %checks {
  return (
    isNamedType(type) &&

The problem was that it was unclear to me that this should be considered a non-overlapping, additional check, and not as a standalone check.

My idea was that type checks (e.g. isInputType, isScalarType, etc.) should be standalone but entity checks (e.g. isSpecifiedDirective) should be guarded by type checks.

But since isIntrospectionType and isSpecifiedScalarType have different symantics it makes sense to change isSpecifiedDirective.

since it makes an isNamedType check internally.

Can you please change it to isScalarType and add tests.

These can now both be used as standalone tests.

Also added some unit tests for these predicates.
@Cito
Copy link
Member Author

Cito commented May 1, 2019

You added them in #1781 😄

Good that git always reminds us of our past sins 😂

I've pushed a new commit that hopefully is better. Both predicates now work as standalone checks.

@Cito
Copy link
Member Author

Cito commented May 1, 2019

Btw, there is another issue here that I don't quite understand.

These checks could all be simplified by using specifiedTypes.some(...), but a comment says this is not possible because " %checks needs a simple expression", so I did not make that simplification.

But what exactly would break here when using some()? It compiles fine, and all tests pass. Maybe it would not be usable as a type guard? But I could not come up with a working example, e.g. this one does not work anyway, for example:

function example(d: GraphQLDirective) {
  if (isSpecifiedDirective(d)) {
    const name: 'include' | 'skip' | 'deprecated' = d.name;  // flow error
  }
}

@IvanGoncharov IvanGoncharov added the PR: bug fix 🐞 requires increase of "patch" version number label May 2, 2019
@IvanGoncharov IvanGoncharov merged commit ccbbb29 into graphql:master May 3, 2019
@IvanGoncharov
Copy link
Member

@Cito Merged 🎉 Thanks for PR 👍

But what exactly would break here when using some()? It compiles fine, and all tests pass. Maybe it would not be usable as a type guard?

By default flow assumes that function arguments are mutable so it can be sure that type argument wasn't changed before some callback called.
But since we switched to @flow strict that ensure arguments are always const Flow is finally happy.
As an experiment, you can change @flow strict => @flow and see error messages.
Fixed #1848, thanks for investigating this 👍

@Cito
Copy link
Member Author

Cito commented May 3, 2019

Thanks for the explanation. Normally you would expect to see more errors when adding "strict", but you're right, in this case the error disappears because with "strict" flow can assume function parameters are const. Setting "strict" seems to pay off, good decision. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: bug fix 🐞 requires increase of "patch" version number
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants