-
Notifications
You must be signed in to change notification settings - Fork 78
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
Some mistakes #1
Comments
Thanks for this! I will update the list with your remarks. I have a comment and a question regarding the two problems you've pointed out:
This example was taken from Flow docs. But I believe you are mistaken about the difference, or at least misunderstood that paragraph was talking about the difference between the syntax of type-casting, not about type assertions.
I'm not sure I understand this. It was described vaguely in the docs. Is it a aupertype of all primitive types, or of all types declared in the whole project, or maybe something else?
What specific features do you believe make Flow much better at type safety than TypeScript? I'm only speaking about TypeScript >= 2.1 with all the safety features enabled, I don't care about older versions. I would say Flow is only marginally better, at some very specific edge cases, but for the 95% of use cases they're equally type-safe. But I'd love to be proven wrong. Thanks again! |
One more thing:
The Flow docs say something else about this:
If you believe this is a mistake in their docs, please open an issue in the Flow repository. |
It's not. You can do this, which is unsafe: function t(x: string | number): string {
var y = x as string;
return y;
}
It means that you can assign anything at all to it. AFAIK the same is true for
Here is a bunch of examples: https://github.com/vkurchatkin/typescript-vs-flow. TypeScript 2.0 didn't change too much, really.
The last sentence seems like an error, yes. But in general the same is true for TypeScript: object can have any other property. |
Here is an example. Given: type User = { name: string, age: number };
function test(user: User) { } This is an error in TS, indeed: const user: User = { name: 'foo', age: 1, nname: '' }; As is this: test({ name: 'foo', age: 1, nname: '' }); But it's not a type system error, more of a linter-type error. const user = { name: 'foo', age: 1, nname: '' };
test(user); The fact that |
@vkurchatkin going through your examples:
Thats the top four examples from the repo you presented. As for differences consider the following where flow doesn't error and ts does (calling a function with additional arguments that it doesn't expect): There are always cases where one handles safety (a fairly objective thought) better than the other, expecially when both are forced to allow things like I wouldn't argue either is technically better than the other. Just the designers have made different choices. PS : Happy holidays 🎅 🌹 |
@basarat everything else except 4 is reflected, although it's probably better to remove examples that work in both.
You are right, it's actually an important distinction. Flow is all about type errors. This is not a type error - in both Flow and Typescript. Something like this might indicate a human error indeed, but it's perfectly legal from type system point of view.
I agree, saying that one is better than the other is pretty meaningless. What I say is that Flow is objectively safer than Typescript, and it's by design. Typescript has other advantages. |
@basarat the last example is an important distinction between Flow and TS. Would you consider adding a paragraph in a PR to this repo? Most of the articles I saw regarding Flowtype vs TS were pretty opinionated and leaned one way or another, I created this repo to make an objective comparison between the two so people can make up their own minds. If any of you spot something that you feel is subjective, please do point it out, I won't bite :). @vkurchatkin I'd be happy to say Flowtype is a bit safer than TS if we had a comprehensive comparison of those cases in which it is true, and those where it is false (I hate claims not supported by arguments). Also, does anybody know how to do a code side-by-side view in GitHub's Markdown? That would probably work much better for these examples. Perhaps we'd need to switch the README format to RST or embed HTML? Happy holidays 🎅 ! |
Here are the most notable examples:
class Animal {}
class Cat extends Animal {
meow() {}
}
class Dog {}
function test(animals: Animal[]) {
animals.push(new Dog());
}
const cats: Cat[] = [];
test(cats);
cats.forEach(cat => cat.meow()); // runtime error
type A = {
a: string | number
};
type B = {
a: string
};
const b: B = { a: 'foo' };
const a: A = b;
a.a = 4;
b.a.toLowerCase(); // runtime error
type B = {
b: number
};
type C = {
a?: () => void,
b: number
};
const a = {
a: 'foo',
b: 1
};
const b: B = a;
const c: C = b;
if (c.a) {
c.a(); // runtime error
}
function test(x: string) {
x.toLowerCase(); // runtime error
}
const a: string | number = 1;
const fn: (x: string | number) => void = test;
fn(a);
classes are structural but treated as nominal class Foo {
foo: string;
}
class Bar {
bar: string;
}
function test(x: Foo | Bar) {
if (x instanceof Foo) {
x.foo.toUpperCase()
} else {
x.bar.toUpperCase(); // runtime error
}
}
test({ foo: 'foo' }); Primitives are subtypes of object types, but treated as if they were not: function test(x: { toLowerCase: () => string } | number) {
if (typeof x === 'object') {
x.toLowerCase();
} else {
x.toFixed(); // runtime error
}
}
test('foo');
class A {
a: string | null;
constructor() {
this.a = 'foo';
}
test() {
this.a = null;
}
}
const a: A = new A();
if (a.a) {
a.test();
a.a.toLowerCase(); // runtime error
} |
Great examples @vkurchatkin, thank you. I'll add it to the comparison when I have a moment to format and describe it. I had no idea about some of those, they seem like bugs in TypeScript and shouldn't happen, but do indeed. @basarat do you know if any of the above problems have related issues in the TypeScript repo? |
Some mistakes I found:
type safety
can't begreat
for both. If Flow isgreat
then TypeScript should begood
.Flow has
never
type, but it's calledempty
.(1 + 1 : number)
is not the same as(1 + 1) as number
. It is 100% safe operation. Flow's equivalent for type assertions is((x:any): number)
.That's not true, object type has the same semantics in Flow and TypeScript.
This is incorrect,
mixed
is a supertype of all types, not just primitives. The closest thing in TypeScript istype mixed = {};
.In Flow:
In Flow:
The text was updated successfully, but these errors were encountered: