そーす

福岡在住のプログラマ

Javascriptの型チェック「Flow」まとめ

flow.org

個人的まとめ。 重要な所だけ読み返したい用です。 型名の和訳は適当です。 時間あったら追記する。

String

let text: string = 'hoge'; //OK
text = 'fuga'; // OK
text = 12;  // Error

let func = (value: string): string => value; // ok

Number

let num: number = 12; //OK
num = 12.3; //OK
num = 'hoge'; // Error

Boolean

let bool: boolean = false; //OK
bool = true; //OK
bool = 1223; //Error

Array

let array: Array<number> = [1, 3]; // OK
array = [3, 4, 4, 4]; // OK
array = ['hoge', 23]; // Error

Symbol

2017/05/18現在では未サポート

null, undefined

let hoge: null = null; // OK
hoge = undefined; // Error
hoge = 22e; // Error

let fuga: void = undefined; // OK
fuga = null; // Error
fuga = 124; // Error

Maybe型

KotlinとかSwiftとかのNullableとかOptionalとかですね

let maybe: ?string = 'text'; // OK
maybe = null; // OK
maybe = undefined; // OK
maybe = 123; // Error

maybe = `text ${maybe}`; // Error
if (maybe != null && maybe !== undefined) {
  maybe = `text ${maybe}`; // OK
}

Union

複数の型を許可します

let union: string | number = 'text'; // OK
union = 123; // OK
union = false; // Error

リテラル

特定の値のみ許可します。 ユニオン型と使うのが良さげ。Enum的な使い方かな?

let only2: 2 = 2; // OK
only2 = 3; // Error

let only2or3: 2 | 3 = 3; // OK
only2or3 = 3; // OK
only2or3 = 4; // Error

Another

i/oを統一したい時とかかな

const func = <T>(value: T): T => value;

Mixed

なんでもイケる。 サボりたい時(違うか

let mi: mixed = '23';
mi = false;
mi = 12;
mi = {};
mi = null;
mi = undefined;

関数のパラメータで使う時は注意が必要

let mFunc: func = (value: mixed) => (`text ${value}`); // Error

// OK
mFunc = (value: mixed) => {
  if (typeof value === 'string') {
    return `text ${value}`;
  }
  return value;
};

typeofなどで型をチェックした場合は以降はその型としてflowが型チェックを行う。 なので、以下はエラー。

mFunc = (value: mixed) => {
  if (typeof value === 'boolean') {
    return value + 'hoge'; // Error
  }
  return value;
};

詳しくは Type Refinements | Flow

Any

基本的に使っちゃ駄目なやつ。flowが完全に無視します。 mixedとの違いですが、

let mFunc: func = (value: any) => (`text ${value}`); // OK

先程mixedだった所を_anyに変えるとflowはエラーを出しません。 極力使用は避けましょう、との事。

関数


// OK
const f1: (value: string) => boolean = (value) => {
  return true;
};

// 実装ではパラメータ名が違っても良い
// OK
const f2: (value: string) => boolean = (v) => {
  return true;
};

// 変数の型はパラメータ無くても良い
// OK
const f3: (string) => boolean = (value) => {
  return true;
};

// 実装の方で型定義してもいい
// OK
const f4 = (value: string): boolean => {
  return true;
};

// どっちも書いても良い
// OK
const f5: (value: string) => boolean = (value: string): boolean => {
  return true;
};

// でも型は合わせないと駄目
// Error
const f6: (value: string) => boolean = (value: boolean): string => {
  return true;
};

// でも変数宣言のパラメータの型がMaybeじゃなくて実装のパラメータがMabyなら大丈夫っぽい。返り値はだめだった
// OK
const f7: (value: string) => boolean = (value: ?string): boolean => {
  return true;
};

// パラメータ名の後に?を付けるとオプションといして扱われる
// OK
const f8: (value?: string) => boolean = (value) => {
  console.log(value);
  return true;
};
f8(); // OK
f8(undefined); // OK
f8('hoge'); // OK
f8(null); // Error

Function

「関数である」ということだけをチェックする。なるべく利用は避けるべきとのこと。

const f9: (Function) => Function = (f: Function) => f(); // OK
f9(() => { })()()(); // OK

オブジェクト

const obj1: {
  foo: boolean,
  bar: ?string,
} =
  {
    foo: true,
    bar: null,
  };

obj1.foo = true; // OK
obj1.bar = 'bar'; // OK
obj1.piyo = null; // Error

厳密なオブジェクト

宣言したオブジェクト型以外のプロパティは許可しない。

const obj2: { foo: boolean, bar: ?string } = { foo: true, bar: null, bar2: 'hoge' }; // OK
const obj3: {|foo: boolean, bar: ?string|} = { foo: true, bar: null, bar2: 'hoge' }; // Error

タイプエイリアス

type NewType = {
  name: string,
  address: ?string,
  tel: number
}

const nType: NewType = {
  name: 'name',
  address: null,
  tel: 0,
};
nType.name = null; // Error
nType.name = 123; // Error
nType.hoge = ''; // Error


type Enum = 0 | 1 | 2;

let en: Enum = 2;
en = 0; // OK
en = 3; // Error

type State = 'state1' | 'state2' | 'state3';

let state: State = 'state1';
state = 'state3'; // OK
state = 'hoge'; // Error

interface

JavaのInterfaceっぽい。 デフォルトの実装は持てない。

interface Hello {
  name: string,
  say(): string
}

class HelloImpl {
  name = 'hogehoge'
  say() {
    return 'hello';
  }
}

class HelloImpl2 implements Hello {
  // nameとsayが実装してないとエラー
  name = 'fugafuga'
  say: () => ''
}

interface Address {
  address: string,
}

class HelloImpl3 implements Hello, Address {
  // nameとsayとaddressが実装してないとエラー
  name = 'fugafuga'
  address = ''
  say: () => ''
}

const h: Hello = new HelloImpl();
h.name
h.say();

const h2: Hello = new HelloImpl2();
h2.name;
h2.say();

const h3: Hello = new HelloImpl3();
h3.name;
h3.address; // Error

const h3_2: Address = new HelloImpl3();
h3_2.name; // Error

interfaceにはread/writeの制限がかけれる。 WriteOnlyの利点が理解出来ないので理解できたら追記する。

interface ReadOnly {
  +name: string
}

const ro: ReadOnly = { name: 'Ryohlan' };
ro.name; // Ok
ro.name = 'hoge'; // Error

interface WriteOnly {
  -name: string,
}

const wo: WriteOnly = { name: 'Ryohlan' };
wo.name = 'hoge'; // OK

ジェネリクス

interface Say<T> {
  name: T,
  say: () => T
}
const f: Say<string> = {
  name: 'hoge',
  say: () => 'hoge',
};
f.name;
f.say();
const f2: Say<number> = {
  name: 1, // OK
  say: () => 'hoge', // Error
};

class Item<T> {
  prop: T;

  constructor(param: T) {
    this.prop = param;
  }

  say(): T {
    return this.prop;
  }
}

const item: Item<string> = new Item('hoge'); /// OK
const item2: Item<string> = new Item(1); // Error


class Item<T: {name: string }> {
  prop: T;

  constructor(param: T) {
    this.prop = param;
  }

  sayName(): string {
    return this.prop.name; // OK
  }

  sayAddress(): string {
    return this.prop.address; // Error
  }
}