Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TS 4.9 compat #587

Merged
merged 5 commits into from
Nov 4, 2022
Merged
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
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { MergeParameters } from './ts47-mergeParameters'
14 changes: 14 additions & 0 deletions src/versionedTypes/package.dist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"typesVersions": {
">=4.7": {
"index": [
"./ts47-mergeParameters.d.ts"
]
},
"<4.7": {
"index": [
"./ts46-mergeParameters.d.ts"
]
}
}
}
Loading