Edited at

FlowのドキュメントにあるコードをひたすらTypeScriptのPlaygroundで試す

More than 1 year has passed since last update.

Flow 0.74

Typescript 2.9

TypeScriptの設定は以下な感じ

image.png


先にまとめ

やってみることによって学びがあった。良かった。


  • FlowのObjectTypeは必要なプロパティがあれば同じ型として使えるがTypeScriptの場合はそうではない。

  • 基本型はだいたい同じだが、mixedがないのとvoidの挙動が違う。

  • Maybe型がない。

  • TypeScriptは型を書かないとanyになるがFlowは推論は推論してくれる

多分、お互いに似たようなことはできるが、実際にやるにはそれなりの経験が必要そうだ。逆をやってみるのも面白そう。

これをやってみた感じからするとFlowのほうが好み。


Stringへの暗黙型変換

https://flow.org/en/docs/types/primitives/#toc-strings

// @flow

"foo" + "foo"; // Works!
"foo" + 42; // Works!
"foo" + {}; // Error!
"foo" + []; // Error!

TypeScriptはエラーにならなかった。+演算子を使うときは気をつけて。


Maybe

https://flow.org/en/docs/types/primitives/#toc-maybe-types

// @flow

function acceptsMaybeString(value: ?string) {
// ...
}

acceptsMaybeString("bar"); // Works!
acceptsMaybeString(undefined); // Works!
acceptsMaybeString(null); // Works!
acceptsMaybeString(); // Works!

残念ながら未対応。自前で実装するしかなさそう。


Mixed

https://flow.org/en/docs/types/mixed/

// @flow

function stringify(value: mixed) {
// ...
}

stringify("foo");
stringify(3.14);
stringify(null);
stringify({});

そもそもmixedが存在しない模様。

Union型で代用できそうな気はする。


Variables


Reassigning variables

https://flow.org/en/docs/types/variables/#toc-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

https://flow.org/en/docs/types/functions/#toc-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

https://flow.org/en/docs/types/functions/#toc-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

https://flow.org/en/docs/types/objects/#toc-exact-object-types

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

https://flow.org/en/docs/types/objects/#toc-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

https://flow.org/en/docs/types/tuples/


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

https://flow.org/en/docs/types/tuples/#toc-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

https://flow.org/en/docs/types/interfaces/

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

https://flow.org/en/docs/types/interfaces/#toc-interface-property-variance-read-only-and-write-only

interface MyInterface {

+covariant: number; // read-only
-contravariant: number; // write-only
}

TypeScriptではreadonlyの書き方は違うし、writeonlyはやり方しらない。


Generic Type

https://flow.org/en/docs/types/generics/

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

https://flow.org/en/docs/types/typeof/#toc-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

https://flow.org/en/docs/types/typeof/#toc-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

https://flow.org/en/docs/types/casting/#toc-type-cast-expression-syntax

(1: number)

TypeScriptだとエラーになった。文法の違い。

1 as number

と書けばいいはず。


Utility Type

TypeScriptは全滅するはず。省略。


Module Types

PlayGroundでは再現する方法がうかばないので、省略