hello-flow

Intro

  • flow ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์Šคํƒœํ‹ฑํ•œ ํƒ€์ž…๋“ค์„ ์ฒดํฌํ•ด์ฃผ๋Š” ๋„๊ตฌ์ด๋‹ค.
  • flow ๋Š” ์ฝ”๋“œ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๋™์•ˆ ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ์„ ์ค๋‹ˆ๋‹ค.
  • flow ๋Š” ํƒ€์ž…์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

install

  1. ๋ฐ”๋ฒจ์„ ์ด์šฉํ•ด์„œ flow ์œ ํ˜•์„ ๋ณ€ํ™˜์‹œ์ผœ์ค˜์•ผ ํ•œ๋‹ค.

    npm install --save-dev babel-cli babel-preset-flow
    • ๋งŒ์•ฝ ๋ฆฌ์—‘ํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์„œ babel-preset-react๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด bable-preset-flow๋Š” ๋ณ„๋„๋กœ ์„ค์น˜ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
  2. .babelrc ์˜ preset ์†์„ฑ์— flow ์ž‘์„ฑ

    {
        "preset": ["flow"]
    }
    • ์ด ์—ญ์‹œ๋„ react๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด react๋งŒ ์ ์–ด์ฃผ๋ฉด ๋œ๋‹ค.
  3. flow ์„ค์น˜

    npm install -g flow-bin
    npm install --save-dev flow-bin
  4. flow init

    • flow init ์„ ํ•˜๊ฒŒ ๋˜๋ฉด .flowconfig ํŒŒ์ผ์ด ์ƒ์„ฑ.
    • .flowconfig์—์„œ flow์˜ ์—ฌ๋Ÿฌ๊ฐ€์ง€ config ์„ค์ •์„ ํ•  ์ˆ˜ ์žˆ์Œ.
    • ํ™ˆํŽ˜์ด์ง€ ์ฐธ์กฐ! https://flow.org/en/docs/config/
  5. flow ๋ช…๋ น์–ด๋ฅผ ์ฐจ๊ธฐ ๋˜๋ฉด ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์˜ ํƒ€์ž…๋“ค์„ ์ฒดํฌ

์š”์•ฝ

  • flow init ์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ init
  • flow ๋ช…๋ น์œผ๋กœ Flow background ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰
  • ๊ฐ ํŒŒ์ผ์ƒ๋‹จ์— "// @flow" ์„ ๊ธฐ์ž…ํ•˜๋ฏ€๋กœ์จ flow๊ฐ€ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ํ• ์ˆ˜ ์žˆ๊ฒŒ ์ •์˜
  • flow code ์ž‘์„ฑ
  • flow error type ์ฒดํฌ

Type๋“ค

  1. primitive types

    • ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์›์‹œํƒ€์ž…์˜ ์ •์˜
    • ๋ฆฌํ„ฐ๋Ÿด ๊ฐ’์˜ ํƒ€์ž…์€ ์†Œ๋ฌธ์ž๋กœ ์ •์˜
    • object๋กœ wrapper ํ•œ๊ฒƒ๋“ค์€ capitalized ๋กœ ์ •์˜
    function method(x: number){}
    
    function method(x: Number){}
    • Boolean , String, Number, null , undefined(flow๋Š” void๋กœ ์ •์˜ ) , Symblos ( ์•„์ง flow๊ฐ€ ์ง€์›์•ˆํ•จ )
  2. Mixed types

    • type์„ ์•Œ์ˆ˜ ์—†์„๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
    • ํ”„๋กœ๊ทธ๋žจ๋“ค์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋‹ค๋ฅธ ํƒ€์ž…์˜ ์ข…๋ฅ˜๋ฅผ ์ง€๋‹์ˆ˜ ์žˆ๋‹ค.
    • mixed ํƒ€์ž…์€ ์•„๋ฌด ํƒ€์ž…์ด๋‚˜ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
    • mixed๋Š” ์•„๋ฌด๊ฑฐ๋‚˜ ๋ฐ›์„ ์ˆ˜ ์žˆ์ง€๋งŒ mixed ์œ ํ˜•์˜ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•œ๋‹ค๋ฉด ์‹ค์ œ ์œ ํ˜•์ด ๋ฌด์—‡์ธ์ง€ ์•Œ์•„๋‚ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ๋ฅผ ๋‚ด๋ฟœ๋Š”๋‹ค.
    function user(value: mixed) {
        return "" + value // error
    }
    
    function user(value: mixed) {
        if( typeof value === "string" ){
            return "" + value // works!!!
        }
    }
  3. any types

    • mixed ์™€ ํ–‡๊ฐˆ๋ ค ํ•˜์ง€ ๋งˆ์„ธ์š”.
    • any ์ž์ฒด๋Š” ์™„๋ฒฝํ•˜๊ฒŒ ์•ˆ์ „ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์–ด๋Š๋•Œ๋‚˜ ์—๋Ÿฌ๋ฅผ ๋‚ด๋ฟœ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • ๊ทธ๋ž˜์„œ any๋กœ type์„ ์ง€์ •ํ–ˆ์„ ๊ฒฝ์šฐ์—๋Š” ๊ฐ€๋Šฅํ•œ ๋นจ๋ฆฌ ๋‹ค๋ฅธ ํƒ€์ž…์œผ๋กœ casting์„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  4. maybe types

    • typing value๊ฐ€ ์žˆ์„์ˆ˜๋„ ์žˆ๊ตฌ ์—†์„์ˆ˜๋„ ์žˆ์„๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด ?number ๋ผ๊ณ  ํƒ€์ž…์„ ์ง€์ •ํ•˜๋ฉด ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์€ number , null , undefined ํƒ€์ž…๋งŒ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
  5. variable types

    • ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ• ๋•Œ ํƒ€์ž…์„ ์ถ”๊ฐ€ํ•œ๋‹ค.
    • javascript ๋ณ€์ˆ˜ ์„ ์–ธ ๋ฐฉ์‹์€ const , let , var ๋ฐฉ์‹์ด ์žˆ๋‹ค.
    • flow๋Š” ๋‘ ๊ทธ๋ฃน์œผ๋กœ ๋‚˜๋‰œ๋‹ค ์žฌ ํ• ๋‹น์ด ๊ฐ€๋Šฅํ•œ์ง€ ( let, var ) ๊ฐ€๋Šฅํ•˜์ง€ ์•Š์€์ง€ ( const )
    var fooVar: number = 2;
    let barLet: number = 2;
    const bar: number = 2;
    
    fooVar = "3" // error
  6. function types

    • ํ•จ์ˆ˜๋Š” 2๊ฐ€์ง€ ์žฅ์†Œ์— type์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜๋‚˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ ( input ) ๋‚˜๋จธ์ง€ ํ•œ๊ณณ์€ return value ( output ) ์ด๋‹ค.
    function concat(a: string , b: string): string {
        return a+b;
    }
    
    let method = (str: string , bool?: boolean, ...args: Array<number>) => {
    
    }
    
    // ํ•จ์ˆ˜ ํƒ€์ž…์„ ์•„์˜ˆ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
    type merlin = {
        ho: (str: string) => void,
        hoing: (string , boolean | void , Array<number>) => void, // ํŒŒ๋ผ๋ฏธํ„ฐ ๋ช…์„ ์ƒ๋žตํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
    }
    // ์ฝœ๋ฐฑ์— ๋Œ€ํ•œ๊ฒƒ๋„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
    function method(cb: (error: Error | null , value: string | null) => void){
    
    }
    • function ์˜ this์˜ ๊ฒฝ์šฐ์—๋Š” ํ•ด๋‹น function์„ ์‹คํ–‰ํ•œ context๋ฅผ ์ฒดํฌํ•œ๋‹ค.
    • ์ˆ ์–ด ํ•จ์ˆ˜์— ๋Œ€ํ•ด์„œ๋Š” ๋ฆฌํ„ด๊ฐ’ ๋‹ค์Œ์— %checks ๋ผ๊ณ  ์ ์–ด์ฃผ์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ๋ฅผ ๋ฟœ๋Š”๋‹ค.
    function truty(a,b):boolean %checks {
        return !!a && !!b;
    }
    
    function merlin(){
        if(truty(a,b)){
    
        }
    }
    • ๋งŒ์•ฝ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ข€๋” ์œ ์—ฐํ•œ function์„ ๋ฐ›์œผ๋ ค๋ฉด () => mixed ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
    function method(func: () => mixed){
    
    }
    • ๋งŒ์•ฝ ํƒ€์ž…์ฒดํฌ๋ฅผ ํ”ผํ•  ํ•„์š”์„ฑ์ด ๋Š๋ผ๋ฉด์„œ any ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉ ํ•˜์ง€ ์•Š์œผ๋ ค๋ฉด Function ์„ ํƒ€์ž…์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋ฐฉ๋ฒ•์€ ์•ˆ์ „ํ•˜์ง€ ์•Š๊ณ  ํ”ผํ•ด์•ผํ•  ๋ฐฉ๋ฒ•์ด๋‹ค.

    ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ์—์„œ ์—๋Ÿฌ๋ฅผ ๋‚ด๋ฟœ์ง€ ์•Š๋Š”๋‹ค.

    function method(func: Function){
        func(1,2); //works
        func("1","2"); //works
        func({},[]) //works
    }
    
    method(function(a: number, b: number){
        return a+b;
    })
  7. Object types

    var obj1: {
        foo: number,
        bar: boolean,
        baz: string
    } = {
        foo: 1,
        bar: true,
        baz: "abc"
    }
    
    var obj: { foo? : boolean} = {};
    obj.foo = true // works
    obj.foo = "abc" // error
    • value ์˜ type์„ ์„ค์ •ํ•  ๋•Œ์—๋Š” optional properties๊ฐ€ void ์™€ ์ƒ๋žต์„ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค. ๋‹ค๋งŒ null ๊ฐ’์€ ์—๋Ÿฌ๋ฅผ ๋‚ธ๋‹ค.
    // foo ์— null ์„ ์…‹ํŒ…ํ•˜๋ฉด error๊ฐ€ ๋œฌ๋‹ค.
    function acceptsObject(value: {foo? : string}){
        // ...
    }
    • sealed object์˜ ๊ฒฝ์šฐ์—๋Š” ์—†๋Š” ๊ฐ’์„ ์ถ”๊ฐ€ ํ•˜๋ ค๋ฉด ์—๋Ÿฌ๋ฅผ ๋ฟœ๋Š”๋‹ค.
    • unsealed object์˜ ๊ฒฝ์šฐ์—๋Š” ์ƒˆ๋กœ์šด ๊ฐ’์„ ์ถ”๊ฐ€ํ•ด๋„ ํ—ˆ๋ฝํ•œ๋‹ค.
    var obj = {
        foo: 1
    }
    
    obj.bar = true; //error
    obj.baz = "abc"; //error
    • ์กฐ๊ฑด๋ฌธ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋Š” ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์ด ์žˆ๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.
    • sealed object ๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ ํ• ์ˆ˜ ์—†๋‹ค.
    var val3: boolean | string = obj.prop;
    • unsealed object ์˜ ์•Œ์ง€๋ชปํ•˜๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ์ •ํ•ด์ง„ ํƒ€์ž…์— ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์€ ์•ˆ์ „ํ•˜์ง€ ๋ชปํ•˜๋‹ค.
    • exact ํ•œ obect๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๋ฉด {| |} ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
    var foo: {| foo: string |} = { foo: "Hello", bar: "World!" }; // Error!
    • flow๋Š” ๊ธฐ๋ณธ object type์—์„œ ์ถ”๊ฐ€ ์†์„ฑ๋“ค์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ƒ๊ฐํ•œ๋‹ค. ์ด๊ฒƒ์„ "width subtyping" ์ด๋ผ ํ•œ๋‹ค.
    function method(obj: { foo: string }) {
      // ...
    }
    
    method({
      foo: "test", // Works!
      bar: 42      // Works!
    });
  8. array types

    • array ํƒ€์ž…์€ Array ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  Type ์žฅ์†Œ์— ๋ฐฐ์—ด์˜ ์š”์†Œ ํƒ€์ž…์„ ์ •์˜ ํ•  ์ˆ˜ ์žˆ๋‹ค.
    let arr: Array<number> = [1, 2, 3];
    let arr2: Array<string> = ["1", "2", "3"]
    • ์ถ•์•ฝํ˜•์œผ๋กœ Type[] ์œผ๋กœ ์ถ•์•ฝํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
    let arr: number[] = [0, 1, 2, 3];
    • ?Type[] ๋Š” ?Array ์™€ ๊ฐ™๊ณ  Array<?T>๋Š” (?Type)[] ๊ณผ ๊ฐ™๋‹ค.
    // ์ด๋ถ€๋ถ„์€ ์ˆซ์ž๋กœ ๋œ ๋ฐฐ์—ด์ด๊ฑฐ๋‚˜ , null , undefined
    
    let arr1: ?number[] = null;   // Works!
    let arr2: ?number[] = [1, 2]; // Works!
    let arr3: ?number[] = [null]; // Error!
    
    // ์ด๋ถ€๋ถ„์€ ๋ฐฐ์—ด์ด๋ฉด์„œ ๋ฐฐ์—ด ์•ˆ์— element๋“ค์ด ์ˆซ์ž์ด๊ฑฐ๋‚˜ , null , undefined ๋œ๊ฑฐ
    let arr1: (?number)[] = null;   // Error!
    let arr2: (?number)[] = [1, 2]; // Works!
    let arr3: (?number)[] = [null]; // Works!
    • array type์„ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
    let array: Array<number> = [0, 1, 2];
    let value: number | void = array[1];
    
    if( value !== undefined ){
        // number
    }
  9. tuple types

    let tuple1: [number] = [1];
    let tuple2: [number, boolean] = [1, true];
    let tuple3: [number, boolean, string] = [1, true, "three"];
    • mutating ํ•œ Array method๋ฅผ tuples type์— ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
    • tuples๋Š” array type๊ณผ match ์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค.
    • ๊ฐ™์€ tuples ํƒ€์ž…์ด๋ผ๋„ ๊ฐ™์€ length ์—ฌ์•ผ ํ•œ๋‹ค.
  10. class types

    • flow ์•ˆ์—์„œ javascript classes ๋Š” ๊ฐ’๊ณผ ํƒ€์ž… 2๊ฐ€์ง€๋กœ ์ž‘๋™ํ•œ๋‹ค.
    class MyClass {
      // ...
      prop: number;
      method(value: string): number {
          this.prop = 42; // ์ด๊ฑธ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์œ„์— ์ฒ˜๋Ÿผ ํ•„๋“œ์— ๋Œ€ํ•œ ํƒ€์ž…์„ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.
      }
    }
    
    let myInstance: MyClass = new MyClass();
    • classes๋Š” ์ž์‹ ๋งŒ์˜ generics๋ฅผ ๊ฐ€์งˆ์ˆ˜ ์žˆ๋‹ค.
    // @flow
    class MyClass<A, B, C> {
      constructor(arg1: A, arg2: B, arg3: C) {
        // ...
      }
    }
    
    var val: MyClass<number, boolean, string> = new MyClass(1, true, 'three');
  11. Type aliases

    • ๋ณต์žกํ•œ ํƒ€์ž…๋“ค์„ ๋‹ค์–‘ํ•œ ์žฅ์†Œ์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„๋•Œ flow๋Š” type alias๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
    type MyObject = {
      foo: number,
      var: boolean,
      baz: string
    }
    • generics ๋ฅผ ํ™œ์šฉํ•ด์„œ ์ •์˜ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
      type MyObject<A, B, C> = {
        foo: A,
        bar: B,
        baz: C,
      };
    
      var val: MyObject<number, boolean, string> = {
        foo: 1,
        bar: true,
        baz: 'three',
      };
  12. Opaque Type Aliases

    • Opaque type aliases๋Š” ์ด ํƒ€์ž…์ด ์ •์˜๋œ ํŒŒ์ผ ์™ธ๋ถ€์— ์žˆ๋Š” ๋‹ค๋ฅธ ํŒŒ์ผ๋“ค์—์„œ ์ ‘๊ทผ์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
    • ์ด ํƒ€์ž…์€ ์„ ์–ธ๋œ ๊ณณ ์–ด๋””์„œ๋“  ์‚ฌ์šฉ๋ ์ˆ˜ ์žˆ๋Š” type aliases์™€ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™ํ•œ๋‹ค.
    opaque type ID = string;
    
    function identity(x: ID): ID {
      return x;
    }
    export type {ID};
    • ๋˜ํ•œ optionallyํ•˜๊ฒŒ ์ œ์•ฝ์กฐ๊ฑด subtyping์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
    // Opaque type alias syntax
    opaque type Alias = Type;
    opaque type Alias: SuperType = Type;
    
    opaque type ID: string = string;
    • import ํ•œ opaque type์€ ์™ธ๋ถ€์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ๋งˆ์น˜ nomial type ์ฒ˜๋Ÿผ ํ–‰๋™ํ•œ๋‹ค.
    • c++ , java , swift๋Š” nomial type ์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•œ๋‹ค.
    • nominal type system ์ด๋ž€ ํƒ€์ž…์˜ ๊ตฌ์กฐ๊ฐ€ ๊ฐ™๋”๋ผ๋„ ์ด๋ฆ„์ด ๋‹ค๋ฅด๋ฉด ์—๋Ÿฌ๋ฅผ ๋ฟœ๋Š”๋‹ค.
    // exports.js
    export opaque type NumberAlias = number;
    
    // imports.js
    import type {NumberAlias} from './exports';
    
    (0: NumberAlias) // Error: 0 is not a NumberAlias!
    
    function convert(x: NumberAlias): number {
      return x; // Error: x is not a number!
    }
    • opaque type alias ์— subtyping constraint๋ฅผ ์ถ”๊ฐ€ํ• ๋•Œ ์šฐ๋ฆฌ๋Š” super type์œผ๋กœ ์‚ฌ์šฉ๋œ opaque type์„ ์„ ์–ธ๋œ ํŒŒ์ผ ๋ฐ–์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
    //exports.js
    export opaque type ID: string = string;
    
    //import.js
    import type {ID} from './exports.js';
    
    function formatID(x: ID): string {
      return "ID: " + x; // works
    }
    
    function toID(x: string): ID {
      return x;
    }
    • subtyping constraint๋ฅผ ํ•จ๊ป˜ ์“ฐ๋Š” opaque type alias ๋ฅผ ๋งŒ๋“ค๋•Œ ํƒ€์ž…์„ค์ •์€ ๋ฐ˜๋“œ์‹œ super type positiona์— ์„ค์ •๋œ ํƒ€์ž…์„ ์ง€๋‹ˆ๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.
    opaque type Bad: string = number; // Error: number is not a subtype of string
    opaque type Good: {x: string} = {x: string, y: number};
  13. Interface Types

    • classes flow type ์˜ ๊ฒฝ์šฐ์—๋Š” nominal typed ์ด๋‹ค. ๋‹ค์‹œ๋งํ•ด์„œ ๊ฐ™์€ ์†์„ฑ๊ณผ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์–ด๋„ ์„œ๋กœ ์ด๋ฆ„์ด ๋‹ค๋ฅธ classes type์€ ํ•œ๊ณณ์—์„œ ๋‹ค๋ฅธ๊ณณ์œผ๋กœ ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€ํ•˜๋‹ค.
    • ๋Œ€์‹ ์—. interface ๋กœ ๊ธฐ๋Œ€ ๋˜๋Š” class structure ๋ฅผ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๋‹ค.
    interface Serializable {
      serialize(): string;
    }
    
    class Foo {
      serialize() { return '[Foo]'; }
    }
    
    class Bar {
      serialize() { return '[Bar]'; }
    }
    
    const foo: Serializable = new Foo(); // Works!
    const bar: Serializable = new Bar(); // Works!
    • implements ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•ด์„œ flow ์—๊ฒŒ ์ด ์ธํ„ฐํŽ˜์ด์Šค์— ๋งค์นญ๋˜๋Š” ํด๋ž˜์Šค๋ฅผ ์›ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋งํ•ด์ค„์ˆ˜๊ฐ€ ์žˆ๋‹ค. ์ด๊ฒƒ์€ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ํด๋ž˜์Šค๋ฅผ ์‰ฝ๊ฒŒ ๋ณ€ํ•˜๊ฒŒ ๋งŒ๋“ค์ง€ ๋ชปํ•˜๋„๋ก ๋ณดํ˜ธํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋ฉ€ํ‹ฐ๋กœ 2๊ฐœ ์ด์ƒ๋„ ์„ค์ •๊ฐ€๋Šฅ
    // @flow
    interface Serializable {
      serialize(): string;
    }
    
    class Foo implements Serializable {
      serialize() { return '[Foo]'; } // Works!
    }
    
    class Bar implements Serializable {
      // $ExpectError
      serialize() { return 42; } // Error!
    }
    • ์ธํ„ฐํŽ˜์ด์Šค syntax๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
    interface MyInterface {
      method(value: string): number;
      property: string;
      property?: string;
    
      [key: string]: number;
    }
    • ์ธํ„ฐํŽ˜์ด์Šค๋„ ๋‹ค๋ฅธ ํƒ€์ž…๊ณผ ๊ฐ™์ด generics๋ฅผ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๊ณ  ํ”„๋กœํผํ‹ฐ์— read-only ์™€ write-only๋ฅผ ์„ค์ •ํ•  ์ˆ˜์žˆ๋‹ค.

      interface MyInterface<A, B, C> {
      foo: A;
      bar: B;
      baz: C;
      }
      
      interface MyInterface {
      +covariant: number // read-only
      -contravariant: number; // write-only
      }
      
      interface Invariant { property: number }
      interface Contravariant { -writeOnly: number }
      
      function method1( value: Invariant) {
      value.property; // works
      value.property = 3.14 // works
      }
      funtion method2 ( value: contravariant) {
      value.property; // error
      value.writeOnly = 3.14 // works!!
      }
      • write-only ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…๋„ pass๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค.
      interface Contravariant { -writeOnly: number }
      var numberOrString = Math.random() > 0.5? 42 : 'forty-two';
      
      var value2: Contravariant = { writeOnly: numberOrString };
  14. Generic Types

    • generic์€ ์ถ”์ƒ์ ์œผ๋กœ ํƒ€์ž…์„ ์ง€์ •ํ• ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
    • generic์€ function , function types , classes , type aliases , interface์— ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค.
    • function ์‚ฌ์šฉ
    function identity<T>(value: T): T {
      return value;
    }
    
    <T>(param: T) => T
    • classes ์‚ฌ์šฉ
    class Item<T> {
      //...
    }
    
    class Item<T> {
      prop: T;
    
      constructor(param: T) {
        this.prop = param;
      }
    
      method(): T {
        return this.prop
      }
    }
    • many generics as you need
    function identity<One, Two, Three>(one: One, two: Two, three: Three) {
    
    }
    • generic ํƒ€์ž…์€ ๋ง ๊ทธ๋Œ€๋กœ "unknown" type์ด๋‹ค. ํ•˜์ง€๋งŒ ํ•จ์ˆ˜ ์•ˆ์—์„œ ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ์—๋Ÿฌ๋ฅผ ๋ฟœ๋Š”๋‹ค.
    function logFoo<T>(obj: T): T {
      console.log(obj.foo); // error
      return obj
    }
    // ์ •ํ™•ํ•œ ํƒ€์ž…์„ ์“ฐ๊ธฐ ์œ„ํ•ด ๋ถ„๊ธฐ๋ฅผ ์ณ์•ผ ํ•œ๋‹ค.
    function logFoo<T>(obj: T): T {
    if (obj && obj.foo) {
      console.log(obj.foo); // Works.
    }
      return obj;
    }
    
    // ๋˜๋Š” ํƒ€์ž…์„ ์ง€์ •ํ•œ๋‹ค.
    function logFoo<T: {foo: string}>(obj: T): T {
      console.log(obj.foo);
      return obj;
    }
    • flow๊ฒฝ์šฐ ํ•˜๋‚˜์˜ ํƒ€์ž…์„ ๋‹ค๋ฅธ๊ณณ์œผ๋กœ ์ „๋‹ฌํ•  ๋•Œ original type์„ ์žƒ์–ด๋ฒ„๋ฆฐ๋‹ค. ๊ทธ๋ž˜์„œ ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์„ ๋œ ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์œผ๋กœ ์ „๋‹ฌํ• ๋•Œ flow ๋Š” "forget" ๋œ๋‹ค. ๊ทธ๊ฒƒ์€ ํ•œ๋•Œ ๊ตฌ์ฒด์ ์ด์˜€๋˜ ๊ฒƒ์ด๋‹ค.
    function identity<T>(val: T): T{
      retur val
    }
    
    let foo: 'foo' = 'foo'; // works
    // identity ํ˜ธ์ถœํ• ๋•Œ ๊ตฌ์ฒด์ ์ธ string์ด ์ „์ž˜ ๋ฌ์ง€๋งŒ ํ˜ธ์ถœ ์ดํ›„์— original type์„ ์žƒ์–ด๋ฒ„๋ฆผ.. ๊ทธ๋ž˜์„œ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋‹ค.
    let bar: 'bar' = identity('bar'); // works
    • generic์€ ํ•จ์ˆ˜์˜ arguments ์ฒ˜๋Ÿผ ํƒ€์ž…์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
    type Item<T> = {
      prop: T,
    }
    
    let item: Item<string> {
      prop: "value"
    }
    • classes ๋ฒ„์ ผ
    class Item<T> {
      prop: T;
      constructor(param: T) {
        this.prop = param;
      }
    }
    
    let item: Item<number> = new Item(42); 
    let item: Item = new Item(42); // error;
    • type aliases ๋ฒ„์ ผ
    type Item<T> = {
      prop: T,
    }
    
    let item1: Item<number> = {prop: 42}
    let item2: Item = {prop: 42}  // error
    • interface ๋ฒ„์ ผ
    interface HasProp<T> {
      prop: T,
    }
    
    class Item {
      prop: string
    }
    (Item.prototype: HasProp<string>); // works
    (Item.prototype: HasProp) // error
    • default ๊ฐ’๋„ ์„ค์ •ํ•  ์ˆ˜์žˆ๋‹ค.
    type Item<T: number = 1> = {
      prop: T,
    };
    
    let foo: Item<> = { prop: 1 };
    let bar: Item<2> = { prop: 2 };
  15. Union types

    • ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํƒ€์ž…์„ ๋ฐ›๊ณ  ์‹ถ๋‹ค๋ฉด Union types๋ฅผ ์“ธ์ˆ˜ ์žˆ๋‹ค.
    • syntax๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
    Type1 | Type2 | ... | TypeN
    • ์—ฌ๋Ÿฌ ํƒ€์ž…์„ (union types)์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” ๊ทธ๋“ค ํƒ€์ž…์ค‘ ํ•˜๋‚˜๋งŒ์„ ๋‹ค๋ค„์•ผ ํ•œ๋‹ค.
    function toStringPrimitives(value: number | boolean | string): string { // Error!
      if (typeof value === 'number') {
        return String(value);
      } else if (typeof value === 'boolean') {
        return String(value);
      }
    }
    • ์—ฌ๋Ÿฌํƒ€์ž…์ค‘ ํ•œ๊ฐ€์ง€ ํƒ€์ž…๋งŒ ๋‹ค๋ฃจ๊ณ  ์‹ถ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•ฉ๋‹ˆ๋‹ค.
    function toStringPrimitives(value: number | boolean | string) {
      if (typeof value === 'number') {
        return value.toLocaleString([], { maximumSignificantDigits: 3 }); // Works!
      }
      // ...
    }
    • ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ ๋‘๊ฐ€์ง€์˜ object types๋“ค์„ union type์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค๋ฉด flow ๋Š” ๋‘ object type ์— ๋“ค์–ด์žˆ๋Š” success property ๋ฅผ base๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์•Œ์•„๋‚ผ์ˆ˜ ์žˆ๋‹ค.
    type Success = {success: true, value: boolean}
    type Fail = {success: false, error: string}
    
    type Response = Success | Fail;
    
    function handleResponse(response: Response) {
      if( response.success ){
        var value: boolean = response.value // work
      } else {
        var error: boolean = response.error // work
      }
    }
    • union type์„ ์œ„์ฒ˜๋Ÿผ ๋ถ„๋ฆฌํ•ด์„œ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ •ํ™•ํ•œ ํƒ€์ž…๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. disjoint unions type์€ ๊ฐ object์—์„œ ํ•œ๊ฐ€์ง€ ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ตฌ๋ณ„๋กœ ์‚ฌ์šฉํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๊ตฌ๋ณ„ ํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœํผํ‹ฐ๊ฐ€ ์—†๋‹ค๋ฉด ์—๋Ÿฌ๋ฅผ ๋ฟœ๊ฒŒ ๋œ๋‹ค. ์ด๊ฒƒ์€ flow๊ฐ€ object type์„ ๋” ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ฐ’์œผ๋กœ ๋ณด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
    • ์ • ์‚ฌ์šฉํ•ด์•ผ ๊ฒ ๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ
    type Success = {| success: true, value: boolean |};
    type Failed  = {| error: true, message: string |};
    
    type Response = Success | Failed;
    
    function handleResponse(response: Response) {
      if (response.success) {
        var value: boolean = response.value;
      } else {
        var message: string = response.message;
      }
    }
  16. Intersection Types

    • & ๋กœ ์—ฐ๊ฒฐ๋œ ํƒ€์ž…๋“ค ์ด๊ฒƒ๋“ค์€ ๋ชจ๋‘๋ฅผ ๋งŒ์กฑํ•ด์•ผ ํ•œ๋‹ค.
    type A = { a: number };
    type B = { b: boolean };
    type C = { c: string };
    
    function method(value: A & B & C) {}
    
    // ExpectError
    method({ a: 1 }); // Error!
    // ExpectError
    method({ a: 1, b: true }); // Error!
    method({ a: 1, b: true, c: 'three' }); // Works!
  17. Typeof Types

    • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ typeof ์—ฐ์‚ฐ์ž์—์„œ ๋ฆฌํ„ด๋˜๋Š” ๊ฐ’์œผ๋กœ ํƒ€์ž…์„ ์ •์˜ํ•œ๋‹ค.
    let num1 = 42;
    let num2: typeof num1 = 3.14;     // Works!
    // $ExpectError
    let num3: typeof num1 = 'world';  // Error!
  18. Type Casting Expressions

    • ํ•จ์ˆ˜๋‚˜ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜์ง€ ์•Š๊ณ  ํƒ€์ž…์„ ์ง€์ •ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋•Œ flow๋Š” inline type cast expression ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    (value: Type)
    
    let val = (value: Type)
    let obj = { prop: (value: Type)}
    let arr = ([(value: Type),(value: Type)]: Array<Type>)
    • ์„ ์–ธ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ• ๋‹น๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค.
    let value = 42;
    
    (value: 42);     // Works!
    (value: number); // Works!
    
    // 42 ํ• ๋‹น ๋ฐ type number
    let newValue = (value: number); 
    
    // $ExpectError
    (newValue: 42);     // Error!
    (newValue: number); // Works!
    • ๋‹ค์Œ ์•„๋ž˜์™€ ๊ฐ™์ด value ๋ฅผ any๋กœ ์บ์ŠคํŒ… ํ•˜๋ฉด, ๋„ˆ๋Š” ์›ํ•˜๋Š” ์–ด๋–ค๊ฒƒ์ด๋“  ํƒ€์ž…์„ ์บ์ŠคํŒ…ํ• ์ˆ˜ ์žˆ๋‹ค. ๋‹ค๋งŒ ์ด๊ฑด ๊ต‰์žฅ์ด ์•ˆ์ „ํ•˜์ง€ ์•Š๋‹ค.
    let newValue = ((value: any): string);
    • ํ•˜์ง€๋งŒ ํƒ€์ž…์„ ์ง€์ •ํ•˜๊ธฐ ์–ด๋ ต๊ณ  ๋ถˆ๊ฐ€๋Šฅํ• ๋•Œ์—๋Š” result์— ๋”ฐ๋ผ์„œ ํƒ€์ž…์ด ์ •ํ•ด์ง€๊ธธ ๋ฐ”๋ž„์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด๋ณด์ž
    // ์–•์€ ๋ณต์‚ฌ
    function cloneObject(obj) {
      const clone = {};
    
      Object.kets(obj).forEach(key => {
        clone[key] = obj[key];
      })
    
      // return clone;
      // ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค.
      return ((clone: any): typeof obj);
    }
    • ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ cloneObject ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ์ „์— ๋“ค์–ด์˜ค๋Š” ์ธ์ž์˜ ํƒ€์ž…์„ ๋จผ์ € ์ •ํ•œ๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ•  ๊ฒƒ์ด๋‹ค.
    function cloneObject(obj: { [key: string]: mixed}){}
    • ํ•˜์ง€๋งŒ ์œ„ ์ฝ”๋“œ๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. ์šฐ๋ฆฌ์˜ typeof obj annotation ๋˜ํ•œ ์ƒˆ๋กœ์šด annotation์„ ๊ฐ–๊ธฐ์— ์ „์ฒด ๋ชฉ์ ์„ ํŒŒ๊ดด์‹œํ‚จ๋‹ค.
    • ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” function ์•ˆ์— ์‚ฌ์šฉํ•  ํƒ€์ž…์— ๋Œ€ํ•ด์„œ assertion ํ•ด์•ผํ•œ๋‹ค.
    function cloneObject(obj) {
      (obj: { [key: string]: mixed});
      //...
    
      return ((clone: any): typeof obj);
    }
    • ์‹ค์งˆ์ ์ธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
    function cloneObject<T: { [key: string]: mixed }> (obj: T): $Shape<T> {
      //...
    }
  19. Utility Types

    • flow ๋Š” flow ์ž์ฒด๋‚ด์— utility types ๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.

    ํ™ˆํŽ˜์ด์ง€ ์ฐธ๊ณ  : https://flow.org/en/docs/types/utilities/

ยฉ 2021 Merlin.ho, Built with Gatsby