Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@
"mocha": "^2.2.5",
"ncp": "^2.0.0",
"nyc": "^6.4.0",
"typescript": "^2.9.2",
"typings-tester": "^0.2.0",
"typescript": "^3.8.3",
"typings-tester": "^0.3.2",
"uglify-js": "^3.0.20"
}
}
24 changes: 17 additions & 7 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export as namespace Reselect;
type $Values<T> = T[keyof T];

export type Selector<S, R> = (state: S) => R;

Expand Down Expand Up @@ -996,12 +997,21 @@ export function createSelectorCreator<O1, O2, O3>(
...rest: any[],
): typeof createSelector;

export function createStructuredSelector<S, T>(
selectors: {[K in keyof T]: Selector<S, T[K]>},
export function createStructuredSelector<
T extends {[key: string]: (state: any) => any},
S = $Values<{[K in keyof T]: Parameters<T[K]>[0]}>
>(
selectors: T,
selectorCreator?: typeof createSelector,
): Selector<S, T>;

export function createStructuredSelector<S, P, T>(
selectors: {[K in keyof T]: ParametricSelector<S, P, T[K]>},
): Selector<S, {[K in keyof T]: ReturnType<T[K]>}>;

export function createStructuredSelector<
T extends {
[key: string]: (state: any, props: any, ...args: any[]) => any;
},
S = $Values<{[K in keyof T]: Parameters<T[K]>[0]}>,
P = Exclude<$Values<{[K in keyof T]: Parameters<T[K]>[1]}>, undefined>,
>(
selectors: T,
selectorCreator?: typeof createSelector,
): ParametricSelector<S, P, T>;
): ParametricSelector<S, P, {[K in keyof T]: ReturnType<T[K]>}>;
85 changes: 64 additions & 21 deletions typescript_test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ function testInvalidTypeInCombinator() {
(state: {testString: string}) => state.testString,
(state: {testString: string}) => state.testString,
(state: {testString: string}) => state.testString,
// typings:expect-error
(state: {testNumber: string}) => state.testNumber,
(state: {testStringArray: string[]}) => state.testStringArray,
// typings:expect-error
(foo1: string, foo2: number, foo3: boolean, foo4: string, foo5: string, foo6: string, foo7: string, foo8: number, foo9: string[]) => {
return {foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9};
});
Expand Down Expand Up @@ -193,7 +193,7 @@ function testParametricSelector() {

const selector = createSelector(
(state: State) => state.foo,
(state: never, props: Props) => props.bar,
(state: State, props: Props) => props.bar,
(foo, bar) => ({foo, bar}),
);

Expand Down Expand Up @@ -225,7 +225,7 @@ function testArrayArgument() {
const selector = createSelector([
(state: {foo: string}) => state.foo,
(state: {foo: string}) => state.foo,
(state: never, props: {bar: number}) => props.bar,
(state: {foo: string}, props: {bar: number}) => props.bar,
], (foo1, foo2, bar) => ({foo1, foo2, bar}));

const ret = selector({foo: 'fizz'}, {bar: 42});
Expand Down Expand Up @@ -323,7 +323,7 @@ function testArrayArgument() {
selector2({foo: 'fizz'}, {bar: 42});

const parametric = createSelector([
(state: never, props: {bar: number}) => props.bar,
(state: {foo: string}, props: {bar: number}) => props.bar,
(state: {foo: string}) => state.foo,
(state: {foo: string}) => state.foo,
(state: {foo: string}) => state.foo,
Expand Down Expand Up @@ -383,7 +383,7 @@ function testDefaultMemoize() {

const memoized2 = defaultMemoize(
(str: string, arr: string[]): {str: string, arr: string[]} => ({str, arr}),
<T>(a: T, b: T, index: number) => {
<T>(a: T & {}, b: T & {}, index: number) => {
if (index === 0)
return a === b;

Expand All @@ -410,7 +410,7 @@ function testCreateSelectorCreator() {

const parametric = createSelector(
(state: {foo: string}) => state.foo,
(state: never, props: {bar: number}) => props.bar,
(state: {foo: string}, props: {bar: number}) => props.bar,
(foo, bar) => ({foo, bar}),
);

Expand All @@ -424,7 +424,7 @@ function testCreateSelectorCreator() {
// typings:expect-error
createSelectorCreator(defaultMemoize, 1);

createSelectorCreator(defaultMemoize, <T>(a: T, b: T, index: number) => {
createSelectorCreator(defaultMemoize, <T>(a: T & {}, b: T & {}, index: number) => {
if (index === 0)
return a === b;

Expand All @@ -433,40 +433,83 @@ function testCreateSelectorCreator() {
}

function testCreateStructuredSelector() {
const selector = createStructuredSelector<{foo: string}, {
foo: string;
bar: number;
}>({
type State = { foo: string };
const FooSelector = (state: State) => state.foo;
const BarSelector = (state: State) => +state.foo;

const selector = createStructuredSelector({
foo: FooSelector,
bar: BarSelector,
});

const selectorGenerics = createStructuredSelector<
{
foo: typeof FooSelector;
bar: typeof BarSelector;
}
>({
foo: state => state.foo,
bar: state => +state.foo,
});

const res = selector({foo: '42'});
const foo: string = res.foo;
const bar: number = res.bar;
type ExpectedResult = {
foo: string;
bar: number;
};

const res: ExpectedResult = selector({foo: '42'});
const resGenerics: ExpectedResult = selectorGenerics({foo: '42'});

// typings:expect-error
selector({bar: '42'});
// typings:expect-error
selectorGenerics({bar: '42'});

// typings:expect-error
selector({foo: '42'}, {bar: 42});

// typings:expect-error
createStructuredSelector<{foo: string}, {bar: number}>({
bar: (state: {baz: boolean}) => 1
selectorGenerics({foo: '42'}, {bar: 42});

createStructuredSelector<{foo: () => string}>({
foo: () => 'string'
});

// typings:expect-error
createStructuredSelector<{foo: string}, {bar: number}>({
bar: state => state.foo
createStructuredSelector<{foo: () => string}>({
foo: () => 1
});

// typings:expect-error
createStructuredSelector<{foo: string}, {bar: number}>({
baz: state => state.foo
createStructuredSelector<{foo: () => string}>({
bar: () => 'string'
});
}

function testParametricCreateStructuredSelector(){
type State = {foo: string; bar: {[key: string]: number}};
const FooSelector = (state: State, id: string) => state.foo;
const BarSelector = (state: State, id: string) => state.bar[id];

const selector = createStructuredSelector({
foo: FooSelector,
bar: BarSelector,
});

type ExpectedResult = {
foo: string;
bar: number;
};

const state: State = {foo: 'foo', bar: {foo: 1, bar: 2}};
const result: ExpectedResult = selector(state, 'foo');

// typings:expect-error
selector(state, 2);

// typings:expect-error
selector({foo: 3}, 2);
}

function testDynamicArrayArgument() {
interface Elem {
val1: string;
Expand Down
3 changes: 1 addition & 2 deletions typescript_test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"strictNullChecks": true
"strict": true
}
}