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

Utility type to access to generic parameter? #6895

Open
DullReferenceException opened this Issue Sep 15, 2018 · 5 comments

Comments

Projects
None yet
3 participants
@DullReferenceException

DullReferenceException commented Sep 15, 2018

Someone asked an interesting question on Stack Overflow:

https://stackoverflow.com/questions/52340343/how-to-to-copy-flow-type-of-properties-from-another-react-component

The problem is that a library exports something of type React.ComponentType<?>, and they want to construct a type derived from ?. The type of the generic parameter is not exported.

Is there a way to get the generic parameter type from a type instance? It seems like this functionality is missing. I'm not even sure how to best express it. When playing around, I tried this:

type PropTypeOf<T: ComponentType<X>> = X;

...but of course that does not work. One way to express could be through a utility function with a parameter index:

type PropTypeOf<T> = $GenericParam<T, 0>;

Providing type-safe generic parameter access seems really useful. Thoughts?

@DullReferenceException DullReferenceException changed the title from Reverse of `Class<T>` utility type or access to generic parameter to Utility type to access to generic parameter? Sep 15, 2018

@RastislavMirek

This comment has been minimized.

Show comment
Hide comment
@RastislavMirek

RastislavMirek Sep 15, 2018

That someone who asked SO question was me. Thank you for creating an issue. I do think this would be very usefull as it would enable safe types for React component's properties where today I can only use {}. React encourages composition of Components rather than inheritance so being able to "copy" properties type from wrapped Component is crucial for being able to correctly type the wrapping Component.

RastislavMirek commented Sep 15, 2018

That someone who asked SO question was me. Thank you for creating an issue. I do think this would be very usefull as it would enable safe types for React component's properties where today I can only use {}. React encourages composition of Components rather than inheritance so being able to "copy" properties type from wrapped Component is crucial for being able to correctly type the wrapping Component.

@wchargin

This comment has been minimized.

Show comment
Hide comment
@wchargin

wchargin Sep 15, 2018

Contributor

In this case, it sounds like you’re looking for $ElementConfig or $ElementProps.

Contributor

wchargin commented Sep 15, 2018

In this case, it sounds like you’re looking for $ElementConfig or $ElementProps.

@wchargin

This comment has been minimized.

Show comment
Hide comment
@wchargin

wchargin Sep 15, 2018

Contributor

The general feature as suggested is a bit problematic, I think. The
parameter is not a property of the type; it is a property of how the
type is constructed. Adding the ability to extract the parameter of
the type would (if it could be made to make sense at all) at the very
least break indiscernibility of identicals.

To see what I mean, consider the following code:

// @flow
type A<T> = {|+x: number, +y: T|};
type B<T> = {|+x: T, +y: string|};
type AString = A<string>;
type BNumber = B<number>;

// Witness: `AString` and `BNumber` are isomorphic.
function f(x: AString): BNumber { return x; }
function g(y: BNumber): AString { return y; }

The types AString and BNumber are exactly the same type. If you
could determine that one of them has type parameter string and the
other has type parameter number, then everything would be broken: you
would have done the same operation to two identical things and gotten
different results.

If the notion of type parameter extraction is restricted to class types,
then this might be less of a problem (I haven’t worked it out). But note
that ComponentType is not a class (it is a union), so this wouldn’t
suffice to solve your original question.

Contributor

wchargin commented Sep 15, 2018

The general feature as suggested is a bit problematic, I think. The
parameter is not a property of the type; it is a property of how the
type is constructed. Adding the ability to extract the parameter of
the type would (if it could be made to make sense at all) at the very
least break indiscernibility of identicals.

To see what I mean, consider the following code:

// @flow
type A<T> = {|+x: number, +y: T|};
type B<T> = {|+x: T, +y: string|};
type AString = A<string>;
type BNumber = B<number>;

// Witness: `AString` and `BNumber` are isomorphic.
function f(x: AString): BNumber { return x; }
function g(y: BNumber): AString { return y; }

The types AString and BNumber are exactly the same type. If you
could determine that one of them has type parameter string and the
other has type parameter number, then everything would be broken: you
would have done the same operation to two identical things and gotten
different results.

If the notion of type parameter extraction is restricted to class types,
then this might be less of a problem (I haven’t worked it out). But note
that ComponentType is not a class (it is a union), so this wouldn’t
suffice to solve your original question.

@RastislavMirek

This comment has been minimized.

Show comment
Hide comment
@RastislavMirek

RastislavMirek Sep 15, 2018

Thank you for pointing me to the right direction. My motivation was solve the specifc problem for which the $ElementConfig and $ElementProps are sufficient.

My suggestion at this point is to docs page with this types more pronounced - it is important but really easy to miss. Maybe even link it from Utility Types page even though I understand that those pages discuss conceptually different topics.

Please feel free to close this issue if Jacob does not object.

RastislavMirek commented Sep 15, 2018

Thank you for pointing me to the right direction. My motivation was solve the specifc problem for which the $ElementConfig and $ElementProps are sufficient.

My suggestion at this point is to docs page with this types more pronounced - it is important but really easy to miss. Maybe even link it from Utility Types page even though I understand that those pages discuss conceptually different topics.

Please feel free to close this issue if Jacob does not object.

@DullReferenceException

This comment has been minimized.

Show comment
Hide comment
@DullReferenceException

DullReferenceException Sep 17, 2018

I was excited to hear about $ElementConfig<C> and went to see how it's implemented, but it seems like it's not written in Flow, but rather OCaml. If the OCaml layer is able to somehow extract these details from an opaque type, why not Flow itself?

I see what you mean about not wanting a type to carry around the details of how it was constructed, but maybe an alternative would be a utility type to tell you what the return type of a constructor/function is. Kind of like $Call<F>, but that doesn't seem to work for class types (or maybe just doesn't work because of the union?). If we could make something like this:

type PropsTypeOf<C> = $PropertyType<$InstanceOf<C>, 'props'>

...that would open up some opportunities.

DullReferenceException commented Sep 17, 2018

I was excited to hear about $ElementConfig<C> and went to see how it's implemented, but it seems like it's not written in Flow, but rather OCaml. If the OCaml layer is able to somehow extract these details from an opaque type, why not Flow itself?

I see what you mean about not wanting a type to carry around the details of how it was constructed, but maybe an alternative would be a utility type to tell you what the return type of a constructor/function is. Kind of like $Call<F>, but that doesn't seem to work for class types (or maybe just doesn't work because of the union?). If we could make something like this:

type PropsTypeOf<C> = $PropertyType<$InstanceOf<C>, 'props'>

...that would open up some opportunities.

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