Skip to content

Commit

Permalink
Merge pull request #587 from reduxjs/feature/ts-4.9-compat
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Nov 4, 2022
2 parents f53eb41 + 11ed107 commit e4c2ace
Show file tree
Hide file tree
Showing 13 changed files with 365 additions and 230 deletions.
2 changes: 1 addition & 1 deletion .eslintrc
Expand Up @@ -45,7 +45,7 @@
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-shadow": ["error"],
"@typescript-eslint/no-shadow": ["off"],
"@typescript-eslint/no-use-before-define": ["error"],
"@typescript-eslint/ban-types": "off",
"prefer-rest-params": "off",
Expand Down
23 changes: 16 additions & 7 deletions .github/workflows/build-and-test-types.yml
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: ['14.x']
node: ['16.x']

steps:
- name: Checkout code
Expand All @@ -16,7 +16,7 @@ jobs:
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: 14.x
node-version: 16.x
cache: 'yarn'

- name: Install dependencies
Expand Down Expand Up @@ -47,8 +47,8 @@ jobs:
strategy:
fail-fast: false
matrix:
node: ['14.x']
ts: ['4.2', '4.3', '4.4', '4.5', '4.6', 'next']
node: ['16.x']
ts: ['4.2', '4.3', '4.4', '4.5', '4.6', '4.7', '4.8', '4.9.2-rc']
steps:
- name: Checkout repo
uses: actions/checkout@v2
Expand All @@ -62,15 +62,24 @@ jobs:
- name: Install deps
run: yarn install

- name: Install TypeScript ${{ matrix.ts }}
run: yarn add typescript@${{ matrix.ts }}

# Build with the actual TS version in the repo
- name: Pack
run: yarn build && yarn pack

- name: Install build artifact
run: yarn add ./package.tgz

# Then install the specific version to test against
- name: Install TypeScript ${{ matrix.ts }}
run: yarn add --dev typescript@${{ matrix.ts }}

- name: 'Remove source to ensure packaged types are used'
run: rm -rf src

# Remove config line that points "reselect" to the `src` folder,
# so that the typetest will use the installed version instead
- run: sed -i -e /@remap-prod-remove-line/d ./typescript_test/tsconfig.json

- name: Test types
run: |
./node_modules/.bin/tsc --version
Expand Down
10 changes: 5 additions & 5 deletions package.json
Expand Up @@ -10,7 +10,7 @@
"typesVersions": {
"<4.2": {
"*": [
"./src/typesVersions/ts4.1/index.d.ts"
"./src/legacyTypes/ts4.1/index.d.ts"
]
}
},
Expand All @@ -26,7 +26,7 @@
},
"scripts": {
"build:commonjs": "cross-env BABEL_ENV=commonjs babel src/*.ts --ignore src/types.ts --extensions .ts --out-dir lib ",
"build:es": "babel src/*.ts --ignore src/types.ts --extensions .ts --out-dir es",
"build:es": "babel src/*.ts --ignore src/types.ts --extensions .ts --out-dir es && cp src/versionedTypes/package.dist.json es/versionedTypes/package.json",
"build:umd": "cross-env NODE_ENV=development rollup -c -o dist/reselect.js",
"build:umd:min": "cross-env NODE_ENV=production rollup -c -o dist/reselect.min.js",
"build:types": "tsc",
Expand Down Expand Up @@ -62,7 +62,7 @@
"@babel/preset-typescript": "^7.15.0",
"@babel/register": "^7.15.3",
"@microsoft/api-extractor": "^7.18.16",
"@reduxjs/toolkit": "^1.6.2",
"@reduxjs/toolkit": "^1.9.0-rc.1",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.0.6",
Expand All @@ -87,13 +87,13 @@
"mocha": "^9.1.3",
"ncp": "^2.0.0",
"nyc": "^15.1.0",
"prettier": "^2.4.1",
"prettier": "^2.7.1",
"react-redux": "^7.2.6",
"rimraf": "^3.0.2",
"rollup": "^2.58.0",
"rollup-plugin-terser": "^7.0.2",
"ts-jest": "27.0.7",
"tslint": "6.1.3",
"typescript": "^4.4.0"
"typescript": "4.8.3"
}
}
1 change: 1 addition & 0 deletions src/index.ts
Expand Up @@ -15,6 +15,7 @@ import type {
export type {
Selector,
GetParamsFromSelectors,
GetStateFromSelectors,
OutputSelector,
EqualityFn,
SelectorArray,
Expand Down
File renamed without changes.
175 changes: 17 additions & 158 deletions src/types.ts
@@ -1,3 +1,6 @@
import type { MergeParameters } from './versionedTypes'
export type { MergeParameters } from './versionedTypes'

/*
*
* Reselect Data Types
Expand Down Expand Up @@ -94,56 +97,6 @@ export type GetParamsFromSelectors<
RemainingItems extends readonly unknown[] = Tail<MergeParameters<S>>
> = RemainingItems

/** Given a set of input selectors, extracts the intersected parameters to determine
* what values can actually be passed to all of the input selectors at once
* WARNING: "you are not expected to understand this" :)
*/
export type MergeParameters<
// The actual array of input selectors
T extends readonly UnknownFunction[],
// Given those selectors, we do several transformations on the types in sequence:
// 1) Extract "the type of parameters" for each input selector, so that we now have
// a tuple of all those parameters
ParamsArrays extends readonly any[][] = ExtractParams<T>,
// 2) Transpose the parameter tuples.
// Originally, we have nested arrays with "all params from input", "from input 2", etc:
// `[ [i1a, i1b, i1c], [i2a, i2b, i2c], [i3a, i3b, i3c] ],
// In order to intersect the params at each index, we need to transpose them so that
// we have "all the first args", "all the second args", and so on:
// `[ [i1a, i2a, i3a], [i1b, i2b, i3b], [i1c, i2c, i3c] ]
// Unfortunately, this step also turns the arrays into a union, and weirder, it is
// a union of all possible combinations for all input functions, so there's duplicates.
TransposedArrays = Transpose<ParamsArrays>,
// 3) Turn the union of arrays back into a nested tuple. Order does not matter here.
TuplifiedArrays extends any[] = TuplifyUnion<TransposedArrays>,
// 4) Find the longest params array out of the ones we have.
// Note that this is actually the _nested_ data we wanted out of the transpose step,
// so it has all the right pieces we need.
LongestParamsArray extends readonly any[] = LongestArray<TuplifiedArrays>
> =
// After all that preparation work, we can actually do parameter extraction.
// These steps work somewhat inside out (jump ahead to the middle):
// 11) Finally, after all that, run a shallow expansion on the values to make the user-visible
// field details more readable when viewing the selector's type in a hover box.
ExpandItems<
// 10) Tuples can have field names attached, and it seems to work better to remove those
RemoveNames<{
// 5) We know the longest params array has N args. Loop over the indices of that array.
// 6) For each index, do a check to ensure that we're _only_ checking numeric indices,
// not any field names for array functions like `slice()`
[index in keyof LongestParamsArray]: LongestParamsArray[index] extends LongestParamsArray[number]
? // 9) Any object types that were intersected may have had
IgnoreInvalidIntersections<
// 8) Then, intersect all of the parameters for this arg together.
IntersectAll<
// 7) Since this is a _nested_ array, extract the right sub-array for this index
LongestParamsArray[index]
>
>
: never
}>
>

/*
*
* Reselect Internal Utility Types
Expand All @@ -153,28 +106,11 @@ export type MergeParameters<
/** Any function with arguments */
export type UnknownFunction = (...args: any[]) => any

/** An object with no fields */
type EmptyObject = {
[K in any]: never
}

type IgnoreInvalidIntersections<T> = T extends EmptyObject ? never : T

/** Extract the parameters from all functions as a tuple */
export type ExtractParams<T extends readonly UnknownFunction[]> = {
[index in keyof T]: T[index] extends T[number] ? Parameters<T[index]> : never
}

/** Extract the return type from all functions as a tuple */
export type ExtractReturnType<T extends readonly UnknownFunction[]> = {
[index in keyof T]: T[index] extends T[number] ? ReturnType<T[index]> : never
}

/** Recursively expand all fields in an object for easier reading */
export type ExpandItems<T extends readonly unknown[]> = {
[index in keyof T]: T[index] extends T[number] ? Expand<T[index]> : never
}

/** First item in an array */
export type Head<T> = T extends [any, ...any[]] ? T[0] : never
/** All other items in an array */
Expand All @@ -191,58 +127,6 @@ export type List<A = any> = ReadonlyArray<A>

export type Has<U, U1> = [U1] extends [U] ? 1 : 0

/** Select the longer of two arrays */
export type Longest<L extends List, L1 extends List> = L extends unknown
? L1 extends unknown
? { 0: L1; 1: L }[Has<keyof L, keyof L1>]
: never
: never

/** Recurse over a nested array to locate the longest one.
* Acts like a type-level `reduce()`
*/
export type LongestArray<S extends readonly any[][]> =
// If this isn't a tuple, all indices are the same, we can't tell a difference
IsTuple<S> extends '0'
? // so just return the type of the first item
S[0]
: // If there's two nested arrays remaining, compare them
S extends [any[], any[]]
? Longest<S[0], S[1]>
: // If there's more than two, extract their types, treat the remainder as a smaller array
S extends [any[], any[], ...infer Rest]
? // then compare those two, recurse through the smaller array, and compare vs its result
Longest<
Longest<S[0], S[1]>,
Rest extends any[][] ? LongestArray<Rest> : []
>
: // If there's one item left, return it
S extends [any[]]
? S[0]
: never

/** Recursive type for intersecting together all items in a tuple, to determine
* the final parameter type at a given argument index in the generated selector. */
export type IntersectAll<T extends any[]> = IsTuple<T> extends '0'
? T[0]
: _IntersectAll<T>

type IfJustNullish<T, True, False> = [T] extends [undefined | null]
? True
: False

/** Intersect a pair of types together, for use in parameter type calculation.
* This is made much more complex because we need to correctly handle cases
* where a function has fewer parameters and the type is `undefined`, as well as
* optional params or params that have `null` or `undefined` as part of a union.
*
* If the next type by itself is `null` or `undefined`, we exclude it and return
* the other type. Otherwise, intersect them together.
*/
type _IntersectAll<T, R = unknown> = T extends [infer First, ...infer Rest]
? _IntersectAll<Rest, IfJustNullish<First, R, R & First>>
: R

/*
*
* External/Copied Utility Types
Expand All @@ -253,32 +137,22 @@ type _IntersectAll<T, R = unknown> = T extends [infer First, ...infer Rest]
* Source: https://github.com/sindresorhus/type-fest/blob/main/source/union-to-intersection.d.ts
* Reference: https://github.com/microsoft/TypeScript/issues/29594
*/
export type UnionToIntersection<Union> = (
export type UnionToIntersection<Union> =
// `extends unknown` is always going to be the case and is used to convert the
// `Union` into a [distributive conditional
// type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
Union extends unknown
? // The union type is used as the only argument to a function since the union
// of function arguments is an intersection.
(distributedUnion: Union) => void
: // This won't happen.
never
// Infer the `Intersection` type since TypeScript represents the positional
// arguments of unions of functions as an intersection of the union.
) extends (mergedIntersection: infer Intersection) => void
? Intersection
: never

/**
* Removes field names from a tuple
* Source: https://stackoverflow.com/a/63571175/62937
*/
type RemoveNames<T extends readonly any[]> = [any, ...T] extends [
any,
...infer U
]
? U
: never
(
Union extends unknown
? // The union type is used as the only argument to a function since the union
// of function arguments is an intersection.
(distributedUnion: Union) => void
: // This won't happen.
never
) extends // Infer the `Intersection` type since TypeScript represents the positional
// arguments of unions of functions as an intersection of the union.
(mergedIntersection: infer Intersection) => void
? Intersection
: never

/**
* Assorted util types for type-level conditional logic
Expand Down Expand Up @@ -313,7 +187,7 @@ type LastOf<T> = UnionToIntersection<
: never

// TS4.1+
type TuplifyUnion<
export type TuplifyUnion<
T,
L = LastOf<T>,
N = [T] extends [never] ? true : false
Expand All @@ -331,21 +205,6 @@ export type ObjValueTuple<
? ObjValueTuple<T, KT, [...R, T[K & keyof T]]>
: R

/**
* Transposes nested arrays
* Source: https://stackoverflow.com/a/66303933/62937
*/
type Transpose<T> = T[Extract<
keyof T,
T extends readonly any[] ? number : unknown
>] extends infer V
? {
[K in keyof V]: {
[L in keyof T]: K extends keyof T[L] ? T[L][K] : undefined
}
}
: never

/** Utility type to infer the type of "all params of a function except the first", so we can determine what arguments a memoize function accepts */
export type DropFirst<T extends unknown[]> = T extends [unknown, ...infer U]
? U
Expand Down
1 change: 1 addition & 0 deletions src/versionedTypes/index.ts
@@ -0,0 +1 @@
export { MergeParameters } from './ts47-mergeParameters'
14 changes: 14 additions & 0 deletions src/versionedTypes/package.dist.json
@@ -0,0 +1,14 @@
{
"typesVersions": {
">=4.7": {
"index": [
"./ts47-mergeParameters.d.ts"
]
},
"<4.7": {
"index": [
"./ts46-mergeParameters.d.ts"
]
}
}
}

0 comments on commit e4c2ace

Please sign in to comment.