Skip to content

Commit

Permalink
chore: narrow-friendly state type
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed May 19, 2024
1 parent 12ea644 commit c9a51e4
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 0 deletions.
32 changes: 32 additions & 0 deletions src/data-state.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expectTypeOf, it } from 'vitest'
import { reactive } from 'vue'
import type { AsyncDataState, DataState } from './data-state'

describe('DataState', () => {
const asyncState = reactive<AsyncDataState<number, Error>>({} as any)
const dataState = reactive<DataState<number, Error>>({} as any)
it.each([asyncState, dataState] as const)('narrowing', (state) => {
if (state.status === 'pending') {
expectTypeOf(state.data).toEqualTypeOf<undefined>()
expectTypeOf(state.error).toEqualTypeOf<null>()
} else if (state.status === 'error') {
expectTypeOf(state.data).toEqualTypeOf<number | undefined>()
expectTypeOf(state.error).toEqualTypeOf<Error>()
} else if (state.status === 'success') {
expectTypeOf(state.data).toEqualTypeOf<number>()
expectTypeOf(state.error).toEqualTypeOf<null>()
}

if (state.error != null) {
expectTypeOf(state.status).toEqualTypeOf<'error'>()
}

if (!state.error && state.status !== 'pending') {
expectTypeOf(state.data).toEqualTypeOf<number>()
}

if (state.data != null) {
expectTypeOf(state.status).toEqualTypeOf<'success' | 'error'>()
}
})
})
60 changes: 60 additions & 0 deletions src/data-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* The status of data.
* - `pending`: initial state
* - `error`: has an error
* - `success`: has data
*/
export type DataStatus = 'pending' | 'error' | 'success'

export interface DataState_Base<TResult, TError> {
/**
* The last successfully resolved data.
*/
data: TResult

/**
* The last rejected error.
*/
error: TError

/**
* The status of the data.
* @see {@link DataStatus}
*/
status: DataStatus
}

export interface DataState_Success<TResult>
extends DataState_Base<TResult, null> {
status: 'success'
}

export interface DataState_Error<TResult, TError>
extends DataState_Base<TResult | undefined, TError> {
status: 'error'
}

export interface DataState_Pending extends DataState_Base<undefined, null> {
status: 'pending'
}

export type DataState<TResult, TError> =
| DataState_Success<TResult>
| DataState_Error<TResult, TError>
| DataState_Pending

/**
* The status of an async operation tied to pinia colada e.g. queries and mutations.
* - `idle`: not running
* - `running`: currently running
*/
export type OperationStatus = 'idle' | 'running'
// TODO: ? - `paused`: waiting to be run

export type AsyncDataState<TResult, TError> = DataState<TResult, TError> & {
/**
* The status of the operation.
* @see {@link OperationStatus}
*/
operationStatus: OperationStatus
}

0 comments on commit c9a51e4

Please sign in to comment.