简体   繁体   中英

Make the type of a field conditionally based on another field type

Is it possible to make a field optional when the other field is of type X?

I want action.meta to be never when the payload is an array, and when it's an object make it { resourceId: string }

const ADD_RESOURCES = 'ADD_RESOURCES'

type Resource = { id: string }

type AddResource<R = any> = {
  type: typeof ADD_RESOURCES
  payload: R extends [] ? Resource[] : (Resource | null)
  meta: R extends [] ? never : { // when payload is an array, make meta never
    resourceId: string
  }
}

const action: Resource = {
  type: ADD_RESOURCES,
  payload: [{ id: '1' }]
  // allow me to omit meta when the payload is array
}

if (Array.isArray(action.payload)) {
  action.meta // make action.meta never
} else {
  action.meta // make action.meta { resourceId: string }
}

You are better off using a union in this case. Since you discriminate by the payload property being an array or not, you can't really use the discriminated union pattern to narrow action and you will need to use a custom type guard:

const ADD_RESOURCES = 'ADD_RESOURCES'

type Resource = { id: string }

type AddResource = {
    type: typeof ADD_RESOURCES
    payload: Resource | null,
    meta: { // when payload is an array, make meta never
        resourceId: string
    }
} | {
    type: typeof ADD_RESOURCES
    payload: Resource[],
    meta: never // you can just omit this.. or type it as undefined 
}

const action: AddResource = {
  type: ADD_RESOURCES,
  payload: [{ id: '1' }]
} as AddResource // Assertion here so flow analasys doesn't just use the actual type of the object literal instead of AddResource

function isArrayResource(r: AddResource): r is Extract<AddResource, { payload: Resource[] }> {
    return Array.isArray(action.payload);
}

if (isArrayResource(action)) {
  action.meta // action.meta never
} else {
  action.meta //  action.meta { resourceId: string }
}

Playground Link

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM