代码之家  ›  专栏  ›  技术社区  ›  Henrik

流中的判别/不相交并集

  •  -2
  • Henrik  · 技术社区  · 7 年前

    Sample errors

    Flow 快乐的

    app/components/commands/types.js:117
    117:   { state: 'state-retries-timeout',
                    ^^^^^^^^^^^^^^^^^^^^^^^ string literal `state-retries-timeout`. Expected string literal `state-initial`, got `state-retries-timeout` instead
     60:   { state: 'state-initial', touched: boolean }
    

    但我不明白我怎么会不遵循 documentation

    // @flow
    
    /**
     * A server-side validation message. Should contain a globally unique key
     * and a message. The key can be used for internationalisation purposes.
     */
    export type ValidationMessage =
      { key: string,
        message: string }
    
    export function validationMessage(key: string, message: string): ValidationMessage {
      return { key, message }
    }
    
    /**
     * The field is untouched in this user-session.
     */
    export const StateInitial = 'state-initial';
    
    /**
     * The edit is not yet persisted anywhere.
     */
    export const StatePending = 'state-pending'
    /**
     * The command is not yet committed on the server, but is committed locally.
     */
    export const StatePendingSavedLocally = 'state-pending-saved-locally'
    /**
     * The command was successfully commited.
     */
    export const StateSuccess = 'state-success'
    /**
     * The command was commited, but there are resulting warnings.
     */
    export const StateSuccessWarnings  = 'state-success-warnings'
    /**
     * The command or its data was invalid and the server returned 400 Bad Request;
     * it may not be retried without changing it.
     */
    export const StateRejected = 'state-rejected'
    
    /**
     * Despite numerous retries, the app failed to persist the change to the server.
     */
    export const StateRetriesTimeout = 'state-retries-timeout'
    
    export type Initial =
      { state: 'state-initial',
        touched: boolean }
    
    export type Pending =
      { state: 'state-pending',
        touched: boolean }
    
    export type PendingSavedLocally =
      { state: 'state-pending-saved-locally',
        touched: boolean }
    
    export type Success =
      { state: 'state-success',
        touched: boolean }
    
    export type SuccessWarnings =
      { state: 'state-success-warnings',
        warnings: ValidationMessage[],
        touched: boolean }
    
    export type Rejected =
      { state: 'state-rejected',
        errors: ValidationMessage[],
        touched: boolean }
    
    export type RetriesTimeout =
      { state: 'state-retries-timeout',
        touched: boolean }
    
    /**
     * The discriminated union of all states we allow fields to be represented as.
     */
    export type ValueChangeState =
      | Initial
      | Pending
      | PendingSavedLocally
      | Success
      | SuccessWarnings
      | Rejected
      | RetriesTimeout
    
    export const initial: ValueChangeState  =
      { state: 'state-initial',
        touched: false }
    
    export const pending: ValueChangeState =
      { state: 'state-pending',
        touched: true }
    
    export const pendingSavedLocally: ValueChangeState =
      { state: 'state-pending-saved-locally',
        touched: true }
    
    export const success: ValueChangeState =
      { state: 'state-success',
        touched: true }
    
    export function successWarnings(warnings: ValidationMessage[], touched: boolean = true): ValueChangeState {
      return {
        state: 'state-success-warnings',
        warnings,
        touched
      }
    }
    
    export function rejected(errors: ValidationMessage[], touched: boolean = true): ValueChangeState {
      return {
        state: 'state-rejected',
        errors,
        touched
      }
    }
    
    export const retriesTimeout: ValueChangeState =
      { state: 'state-retries-timeout',
        touched: true }
    

    示例用法

    // @flow
    /*eslint no-unused-expressions: 0 */
    import { expect } from 'chai';
    import { describe, it } from 'mocha';
    import { initial, pending } from './types';
    import { valueStateReducer } from './reducers';
    import { successOf, rejectionOf, timeoutOf, successWarningsOf } from './actions';
    
    describe('(Reducer) components/commands', function() {
      describe('valueStateReducer', function() {
        const noop = () => ({ type: 'NOOP' });
        const testing = () => ({ type: 'commands/TESTING' });
        const subject = valueStateReducer('commands/TESTING');
    
        it('other makes no change', function() {
          const res = subject(initial, noop());
          expect(res).to.deep.eq(initial);
        });
    
        it('of action makes pending', function() {
          const res = subject(initial, testing());
          expect(res).to.deep.eq(pending);
        });
      });
    });
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   Henrik    7 年前

    答案是,当还提供默认值时,flow不能正确处理可为空/未定义的判别并集/和类型的破坏性赋值。

    assignment bug

    valueState initial (DU案例之一)。这会触发错误。

    如果我将默认值进一步降低,则流不会呕吐:

    bug workaround