Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
11
Help us understand the problem. What is going on with this article?
@eielh

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

More than 3 years have passed since last update.

Flow 0.74
Typescript 2.9

TypeScriptの設定は以下な感じ

image.png

先にまとめ

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

  • 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では再現する方法がうかばないので、省略

11
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
11
Help us understand the problem. What is going on with this article?