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

Write more about Object definition differences for the same code #8

Open
niieani opened this issue Jan 29, 2017 · 9 comments
Open

Write more about Object definition differences for the same code #8

niieani opened this issue Jan 29, 2017 · 9 comments

Comments

@niieani
Copy link
Owner

niieani commented Jan 29, 2017

[DRAFT]

In TS you don't get "loose" properties on your objects, since you can't assign more than your definition expects:

/* @flow */
type NonExact = {}
const a : NonExact = { extraProp: '123' } // error in TS, error in Flow if: type Exact = $Exact<{}>

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.

@vkurchatkin
Copy link

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

@niieani
Copy link
Owner Author

niieani commented Feb 10, 2017

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 })

@vkurchatkin
Copy link

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);

@niieani
Copy link
Owner Author

niieani commented Feb 10, 2017

@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:

  1. best: use Flow's exact object types: {| ... |}
  2. good: use TypeScript's default object types (catches a few less errors than Flow's exact, but a few more than Flow's default)
  3. ok: Flow's default object types

@vkurchatkin
Copy link

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.

@niieani
Copy link
Owner Author

niieani commented Feb 10, 2017

@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?

@vkurchatkin
Copy link

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

@RyanCavanaugh
Copy link

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

@niieani
Copy link
Owner Author

niieani commented Jun 6, 2017

@RyanCavanaugh I agree, I'll add this. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants