Open niieani opened 7 years ago
just that this default is less safe than in TS
It's not less safe, it provides exactly the same amount of safety. There is nothing unsafe in having extra properties
There is nothing unsafe in having extra properties.
I have to disagree here.
export type Car = {
brand: string,
color?: 'red' | 'blue',
}
const car : Car = { brand: 'Tesla', colour: 'red' }
In TypeScript you'd spot the typo immediately (color
=> colour
), therefore I maintain that expecting types to be exact is a more type-safe behavior. You can do that in Flow (with {| ... |}
syntax), but for this specific feature, Flow is less safe by default than TypeScript.
Another example where it is less safe:
function storeInDatabase(car: Car) {
db.insert(car)
// I can be sure that 'car'
// has only the expected properties
// nothing more, nothing less
// since nothing can ever be added by mistake
}
storeInDatabase({ brand: 'Tesla', color: 'red', thing: 123 })
Right, this is a typo, but it's unsafe, it doesn't lead to runtime errors.
I maintain that expecting types to be exact is a more type-safe behavior.
Once again, objects are not exact in Typescript. Exact means that it is guaranteed that object doesn't have properties other than declared. This is a unique feature of Flow.
Typescript catches additional properties when "fresh" object is assigned to annotated variable or passed to a function directly. Other than that it allows additional properties and doesn't guarantee anything.
Using your example:
type Car = {
brand: string,
color: string,
}
function storeInDatabase(car: Car) {
db.insert(car)
// I can be sure that 'car'
// has only the expected properties
// or can I?
}
const car = { brand: 'Tesla', color: 'red', thing: 123 };
storeInDatabase(car);
@vkurchatkin ok, that clears it up for me. Many thanks.
I don't think 'no runtime errors' should be the end-goal though. I think type safety, like strictness of definitions (and not allowing additional properties) could save us headaches in many cases like those cited above. Given that, it's a shame Flow doesn't use the safer, strict / closed types by default, and only allows to have "open" types when explicitly defined.
TypeScript default is still a tiny bit better than the Flow one, so in order from best safety to worst:
{| ... |}
Preventing typos is non-goal for Flow, but it is for Typescript. "No runtime errors" is the definitions of safety. Exact types are useful only in a limited amount of cases, because they are not subtype-able. Saying that that exact objects are just better is the same as saying that not having subtyping is better.
@vkurchatkin I get that Flow's definition of safety is "no runtime errors", but I don't think that covers it. To me, typos are just as part of programming safety, as are extraneous properties - and if we're being specific, both of those can cause runtime errors.
E.g. to reference the previous case: a database driver throwing an error after being passed an object with an extraneous, unexpected field present. Definitely not an edge case, and it classifies as a runtime error, no? Unless we use the comfortable definition of "only built-in errors", which is, to be honest kinda cheating.
Couldn't exact types could work with subtyping, if one could use destructuring syntax to extend types, and/or if you could annotate a non-exact version of the object for explicit non-exact use?
You confuse safety with correctness.
both of those can cause runtime errors
No, they cannot. That's the point.
a database driver throwing an error after being passed an object with an extraneous, unexpected field present.
Well, first of all, this is actually covered but Flow's exact type. Secondly, there is no way to prevent a library from throwing an error if it wants to. You can't encode every possible runtime check as a type.
Couldn't exact types could work with subtyping, if one could use destructuring syntax to extend types, and/or if you could annotate a non-exact version of the object for explicit non-exact use?
Not sure what you mean
Perhaps there's a useful row in the initial table based on this discussion
TypeScript | Flow | |
---|---|---|
Design Goal | Identify errors in programs | Enforce type safety |
@RyanCavanaugh I agree, I'll add this. Thanks.
[DRAFT]
In TS you don't get "loose" properties on your objects, since you can't assign more than your definition expects:
TS's above object's-exact-by-default stance might get you to design better APIs, unless you want to go and slap $Exact everywhere in your Flow code. This doesn't mean Flow is less-type-safe, it's just that this default is less safe than in TS. There are other cases where the opposite is true (TS's defaults being weaker-typed) then Flow.