Skip to content

Commit

Permalink
change inference behaviour in old TS versions
Browse files Browse the repository at this point in the history
  • Loading branch information
phryneas committed Sep 9, 2019
1 parent 16e0686 commit 5e215ed
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 3 deletions.
12 changes: 11 additions & 1 deletion src/createAction.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Action } from 'redux'
import { IsUnknownOrNonInferrable } from './tsHelpers'

/**
* An action with a string type and an associated payload. This is the
Expand Down Expand Up @@ -46,7 +47,16 @@ export type ActionCreatorWithoutPayload<
export type ActionCreatorWithPayload<
P,
T extends string = string
> = WithTypeProperty<T, <PT extends P>(payload: PT) => PayloadAction<PT, T>>
> = WithTypeProperty<
T,
IsUnknownOrNonInferrable<
P,
// TS < 3.5 infers non-inferrable types to {}, which does not take `null`. This enforces `undefined` instead.
<PT extends unknown>(payload: PT) => PayloadAction<PT, T>,
// default behaviour
<PT extends P>(payload: PT) => PayloadAction<PT, T>
>
>

/**
* An action creator that produces actions with a `payload` attribute.
Expand Down
36 changes: 36 additions & 0 deletions src/tsHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// taken from https://github.com/joonhocho/tsdef
// return True if T is `any`, otherwise return False
export type IsAny<T, True, False = never> = (
| True
| False) extends (T extends never ? True : False)
? True
: False

// taken from https://github.com/joonhocho/tsdef
// return True if T is `unknown`, otherwise return False
export type IsUnknown<T, True, False = never> = unknown extends T
? IsAny<T, False, True>
: False

export type IsEmptyObj<T, True, False = never> = T extends any
? {} extends T
? IsUnknown<T, False, IsAny<T, False, True>>
: False
: never

/**
* returns True if TS version is above 3.5, False if below.
* uses feature detection to detect TS version >= 3.5
* * versions below 3.5 will return `{}` for unresolvable interference
* * versions above will return `unknown`
* */
export type AtLeastTS35<True, False> = IsUnknown<
ReturnType<<T>() => T>,
True,
False
>

export type IsUnknownOrNonInferrable<T, True, False> = AtLeastTS35<
IsUnknown<T, True, False>,
IsEmptyObj<T, True, False>
>
31 changes: 29 additions & 2 deletions type-tests/files/createSlice.typetest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,10 @@ function expectType<T>(t: T) {
expectType<number>(counter.actions.concatMetaStrLen('test').meta)

// typings:expect-error
expectType<string>(counter.actions.strLen('test').payload)
expectType<string>(counter.actions.incrementByStrLen('test').payload)

// typings:expect-error
expectType<string>(counter.actions.strLenMeta('test').meta)
expectType<string>(counter.actions.concatMetaStrLen('test').meta)
}

/*
Expand All @@ -169,3 +169,30 @@ function expectType<T>(t: T) {
}
})
}


/*
* Test: if no Payload Type is specified, accept any payload
* see https://github.com/reduxjs/redux-starter-kit/issues/165
*/
{
const initialState = {
name: null
};


const mySlice = createSlice({
initialState,
reducers: {
setName: (state, action) => {
state.name = action.payload;
}
}
});

const x = mySlice.actions.setName;

mySlice.actions.setName(null);
mySlice.actions.setName("asd");
mySlice.actions.setName(5);
}

0 comments on commit 5e215ed

Please sign in to comment.