Flow 0.74
Typescript 2.9
TypeScriptの設定は以下な感じ
先にまとめ
やってみることによって学びがあった。良かった。
- FlowのObjectTypeは必要なプロパティがあれば同じ型として使えるがTypeScriptの場合はそうではない。
- 基本型はだいたい同じだが、mixedがないのとvoidの挙動が違う。
- Maybe型がない。
- TypeScriptは型を書かないとanyになるがFlowは推論は推論してくれる
多分、お互いに似たようなことはできるが、実際にやるにはそれなりの経験が必要そうだ。逆をやってみるのも面白そう。
これをやってみた感じからするとFlowのほうが好み。
Stringへの暗黙型変換
// @flow
"foo" + "foo"; // Works!
"foo" + 42; // Works!
"foo" + {}; // Error!
"foo" + []; // Error!
TypeScriptはエラーにならなかった。+演算子を使うときは気をつけて。
Maybe
// @flow
function acceptsMaybeString(value: ?string) {
// ...
}
acceptsMaybeString("bar"); // Works!
acceptsMaybeString(undefined); // Works!
acceptsMaybeString(null); // Works!
acceptsMaybeString(); // Works!
残念ながら未対応。自前で実装するしかなさそう。
Mixed
// @flow
function stringify(value: mixed) {
// ...
}
stringify("foo");
stringify(3.14);
stringify(null);
stringify({});
そもそもmixedが存在しない模様。
Union型で代用できそうな気はする。
Variables
Reassigning variables
let foo = 42;
if (Math.random()) foo = true;
if (Math.random()) foo = "hello";
let isOneOf: number | boolean | string = foo; // Works!
TypeScriptでは代入した時点で foo が numberと決まってしまう。true や "hello"は代入しようとすると型エラーに。flowは型の再設定が可能で、Union Typeになる。
以下の様に書いてあげるとエラーは消える
let foo: number | boolean | string = 42;
if (Math.random()) foo = true;
if (Math.random()) foo = "hello";
let isOneOf: number | boolean | string = foo; // Works!
Function this
function method() {
return this;
}
var num: number = method.call(42);
// $ExpectError
var str: string = method.call(42);
TypeScriptでは暗黙のanyでエラーになる。
TypeScriptなら以下でどうだろうか。
function method<T>(this: T): T {
return this;
}
var num: number = <number>method.call(42);
// $ExpectError
var str: string = <number>method.call(42);
Predicate Functions
function truthy(a, b): boolean %checks {
return !!a && !!b;
}
そもそも %checks
なぞTypeScriptにない。
TypeScriptでの解決方法は浮かばず。
Unsealed Object
// @flow
var obj = {};
obj.foo = 1; // Works!
obj.bar = true; // Works!
obj.baz = 'three'; // Works!
これも同じようにする方法がわからず。すべてのkeyとその型を書く以外の方法がわからなかった。
Exact Object
// @flow
var foo: {| foo: string |} = { foo: "Hello", bar: "World!" }; // Error!
TypeScriptには無い文法だが、interface
を使うか
// @flow
interface Hoge { foo: string }
var foo: Hoge = { foo: "Hello", bar: "World!" }; // Error!
Objects as maps
// @flow
var o: { [string]: number } = {};
o["foo"] = 0;
o["bar"] = 1;
var foo: number = o["foo"];
TypeScriptは文法が違う。labelをつけてあげればよい。labelをつけてもFlowでは動作する。
// @flow
var o: { [key: string]: number } = {};
o["foo"] = 0;
o["bar"] = 1;
var foo: number = o["foo"];
Tuple type
// @flow
let tuple: [number, boolean, string] = [1, true, "three"];
let none: void = tuple[3]; // error
TypeScriptでは別の要因でエラーになる。tuple[3]の利用にはエラーがでない。
// @flow
let tuple: [number, boolean, string] = [1, true, "three"];
let none: string | number | boolean = tuple[3]; // error
これだとエラーにならないので、Tupleというより配列として認識している。
strictly enforced tuple length arity
// @flow
let tuple: [number, number] = [1, 2];
// $ExpectError
let array: Array<number> = tuple; // Error!
TypeScriptではエラーにならない。tupleではなく配列として扱われるため。
// @flow
let tuple: [number, number] = [1, 2];
tuple.join(', '); // Works!
// $ExpectError
tuple.push(3); // Error!
これも同様。
Opaque Type Aliases
opaque type ID = string;
TypeScriptは対応していない。がんばれば似たようなことはできるはず。
Interface Types
// @flow
class Foo {
serialize() { return '[Foo]'; }
}
class Bar {
serialize() { return '[Bar]'; }
}
// $ExpectError
const foo: Foo = new Bar(); // Error!
TypeScriptではエラーにならない。
構造的部分型になるため。Flowでは別の型として扱われる。
根本的な思想の違いと考えられる。
Flowでやるなら共通インターフェイスを用意する
// @flow
interface Base {
serialize(): string;
}
class Foo {
serialize() { return '[Foo]'; }
}
class Bar {
serialize() { return '[Bar]'; }
}
// $ExpectError
const foo: Base = new Bar(); // Error!
foo.serialize();
read only and write only
interface MyInterface {
+covariant: number; // read-only
-contravariant: number; // write-only
}
TypeScriptではreadonlyの書き方は違うし、writeonlyはやり方しらない。
Generic Type
// @flow
type IdentityWrapper = {
func<T>(T): T
}
function identity(value) {
return value;
}
function genericIdentity<T>(value: T): T {
return value;
}
// $ExpectError
const bad: IdentityWrapper = { func: identity }; // Error!
const good: IdentityWrapper = { func: genericIdentity }; // Works!
TypeScriptだとidentityが暗黙のanyでエラーになってしまう。
下記のコードで代用してみたが、badはエラーにならなかった。
type IdentityWrapper = {
func<T>(t: T): T
}
function identity(value: any): any {
return value;
}
function genericIdentity<T>(value: T): T {
return value;
}
// $ExpectError
const bad: IdentityWrapper = { func: identity }; // Error!
const good: IdentityWrapper = { func: genericIdentity }; // Works!
型の制約
// @flow
function logFoo<T: { foo: string }>(obj: T): T {
console.log(obj.foo); // Works!
return obj;
}
logFoo({ foo: 'foo', bar: 'bar' }); // Works!
// $ExpectError
logFoo({ bar: 'bar' }); // Error!
TypeScriptだと制約の付け方が違う。
extends
で指定できる
// @flow
function logFoo<T extends { foo: string } >(obj: T): T {
console.log(obj.foo); // Works!
return obj;
}
logFoo({ foo: 'foo', bar: 'bar' }); // Works!
// $ExpectError
logFoo({ bar: 'bar' }); // Error!
リテラル値のあるオブジェクトのUnion Type
// @flow
type Success = { success: true, value: boolean };
type Failed = { success: false, error: string };
type Response = Success | Failed;
function handleResponse(response: Response) {
if (response.success) {
var value: boolean = response.value; // Works!
} else {
var error: string = response.error; // Works!
}
}
この辺はオブジェクト型の問題なのでどうしようもない。
以下のようにしてもだめだった。
// @flow
interface Success {
success: true,
value: boolean,
}
interface Failed {
success: false,
error: string,
}
type Response_ = Success | Failed;
function handleResponse(response: Response_) {
if (response.success) {
var value: boolean = response.value; // Works!
} else {
var error: string = response.error; // Works!
}
}
typeof
typeof type syntax
// @flow
let num1 = 42;
let num2: typeof num1 = 3.14; // Works!
// $ExpectError
let num3: typeof num1 = 'world'; // Error!
let bool1 = true;
let bool2: typeof bool1 = false; // Works!
// $ExpectError
let bool3: typeof bool1 = 42; // Error!
let str1 = 'hello';
let str2: typeof str1 = 'world'; // Works!
// $ExpectError
let str3: typeof str1 = false; // Error!
let bool2: typeof bool1 = false; // Works!
がエラーになる。 bool1 の型はtrueと認識してしまう。どうしようもなさそう。
Typeof inherits behaviors of other types
// @flow
class MyClass {
method(val: number) { /* ... */ }
}
class YourClass {
method(val: number) { /* ... */ }
}
// $ExpectError
let test1: typeof MyClass = YourClass; // Error!
let test2: typeof MyClass = MyClass; // Works!
これまでの傾向からわかってるが一応。TypeScriptではエラーにならない。
Type Cast
(1: number)
TypeScriptだとエラーになった。文法の違い。
1 as number
と書けばいいはず。
Utility Type
TypeScriptは全滅するはず。省略。
Module Types
PlayGroundでは再現する方法がうかばないので、省略