These code structure are something borrowed from GraphQL (like: GraphQLInputType)
type List<T> = { data:T };
type Scalar = number|string|boolean;
type P = Scalar | List<Scalar>;
given those type, are there some good way to direct cast List<number>
to P
?
I tried some method to write correctly code , but get a bit confused.
Currently, seems Flow treat types passed into polymorphic T
as a integrated type, and did not try to match them.
So fn
will fail.
But can use fn1
to around it. but actually this method has no benefit, except increasing redundancy codes .
And especially consider fn3
, a { data:2 }
should be { data:number }
, but when typed it ,Flow will fail. If a directly { data:2 }
is acceptable, should its type also be accepted?
detail codes:
flowtype.org/try
/* @flow */
type List<T> = {
data:T
};
type Scalar = number|string|boolean;
type P = Scalar | List<Scalar>;
function fn(v: List<number|string>):P {
return v; // error
}
function fn1(v: List<number|string>):P {
return { data: v.data };
}
function fn2():P {
return { data:2 };
}
function fn3():P {
const data:{data:number} = { data:2 };
return data; // error, but should { data:2 } not be a {data:number}?
}
function fn4():P {
const data:List<*> = { data:2 };
return data;
}
function fn5(v: number):P {
return { data:v };
}
If number | string
is a subtype of number
, why isn't List<number | string>
a subtype of List<number>
?
The problem is that you're assuming that data
is read-only, but that's not being enforced by anything. In theory, you could write code like this:
const numList: List<number> = { data: 3 };
const numOrStringList: List<number | string> = numList; // If we let you do this
numOrStringList.data = "hello"; // Then you could do this
(numList.data: number); // And now this is wrong
So what's missing is the ability to say that the data
property is read-only. @samwgoldman is actually working on this right now! Once that launches, Flow will be able to treat the T
in List<T>
as covariant.
! Thank you , The const feature looks awesome!
i think maybe i have some misunderstanding to Flow's polymorphic
.
previously i think i wrote a more weird way to solve this( but now seems it is the right semantic in Flow)
type P<T: number|string> = List<T>;
function ts(v: List<number>):P<*> {
return v;
}
Actually those problem are something condensed from here -> graphql/ definition.js#L55
export type GraphQLInputType =
GraphQLScalarType |
GraphQLEnumType |
GraphQLInputObjectType |
GraphQLList<GraphQLInputType> |
GraphQLNonNull<
GraphQLScalarType |
GraphQLEnumType |
GraphQLInputObjectType |
GraphQLList<GraphQLInputType>
>;
I think when this new feature landed , some of those code could be restruct well. <T: number|string>
seems too cumbersome for some mini code.
@gabelevi @samwgoldman Im still a bit confused of how to express polymorphic type recursively rightly. (though when the const feature landed, seems there maybe have a road to around it).
use a union can express a recursively expression, but how to express a type recursively in polymorphic ? if i want to express recursively type like List<List<NonNull<List<List<number>>>>>>
, a union type can simply express like below : (though get a wrong result).
type Scalar = number|string;
type InputType =
Scalar|
List< InputType >| // recursively, but get wrong behavior
// It make something like : List<number|string|List|NonNull>
// but i want a List<number>|List<string>|List<List>|List<NonNull>
NonNull< Scalar| List<InputType> >; // Its same error here.
Seems in polymorphism, it can be expanded by hand(and be used like fn<T: InputType2>
), but i dont know how to express a recursion type in below style.
type InputType2 =
Scalar |
List<number> | List<string> | //List< List(how to express a recursion here?) >|
NonNull<number> | NonNull<string> |
NonNull< List<number> >; // same here ,dont know how to express recursively
Are there same style to express this rightly?
Most helpful comment
If
number | string
is a subtype ofnumber
, why isn'tList<number | string>
a subtype ofList<number>
?The problem is that you're assuming that
data
is read-only, but that's not being enforced by anything. In theory, you could write code like this:So what's missing is the ability to say that the
data
property is read-only. @samwgoldman is actually working on this right now! Once that launches, Flow will be able to treat theT
inList<T>
as covariant.