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 3.4 inference improvements #348
Changes from 3 commits
06c4b3b
074c3dd
d4f5486
3644aa8
69dc236
625f606
a25e6e5
f7769d7
3fdfcc6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
type Tail<T extends any[]> = ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) | ||
? TT | ||
: [] | ||
|
||
/** Object types that should never be mapped */ | ||
type AtomicObject = | ||
| Function | ||
|
@@ -12,28 +16,19 @@ type AtomicObject = | |
| Number | ||
| String | ||
|
||
type ArrayMethod = Exclude<keyof [], number> | ||
type Indices<T> = Exclude<keyof T, ArrayMethod> | ||
|
||
export type DraftArray<T extends ReadonlyArray<any>> = Array< | ||
{[P in Indices<T>]: Draft<T[P]>}[Indices<T>] | ||
> | ||
|
||
export type DraftTuple<T extends ReadonlyArray<any>> = { | ||
[P in keyof T]: P extends Indices<T> ? Draft<T[P]> : never | ||
} | ||
export type Draft<T> = T extends AtomicObject | ||
? T | ||
: T extends object | ||
? { -readonly [K in keyof T]: Draft<T[K]> } | ||
: T // mostly: unknown & any | ||
|
||
export type Draft<T> = T extends never[] | ||
? T | ||
: T extends ReadonlyArray<any> | ||
? T[number][] extends T | ||
? DraftArray<T> | ||
: DraftTuple<T> | ||
: T extends AtomicObject | ||
? T | ||
: T extends object | ||
? {-readonly [P in keyof T]: Draft<T[P]>} | ||
: T | ||
/** Convert a mutable type into a readonly type */ | ||
export type Immutable<T> = | ||
T extends AtomicObject | ||
? T | ||
: T extends object | ||
? { readonly [K in keyof T]: Immutable<T[K]> } | ||
: T | ||
|
||
export interface Patch { | ||
op: "replace" | "remove" | "add" | ||
|
@@ -52,26 +47,6 @@ export type Produced<Base, Return> = Return extends void | |
: Return extends Promise<infer Result> | ||
? Promise<Result extends void ? Base : FromNothing<Result>> | ||
: FromNothing<Return> | ||
|
||
type ImmutableArray<T extends ReadonlyArray<any>> = { | ||
[P in Extract<keyof T, number>]: ReadonlyArray<Immutable<T[number]>> | ||
}[Extract<keyof T, number>] | ||
|
||
type ImmutableTuple<T extends ReadonlyArray<any>> = { | ||
readonly [P in keyof T]: Immutable<T[P]> | ||
} | ||
|
||
/** Convert a mutable type into a readonly type */ | ||
export type Immutable<T> = T extends object | ||
? T extends AtomicObject | ||
? T | ||
: T extends ReadonlyArray<any> | ||
? Array<T[number]> extends T | ||
? ImmutableArray<T> | ||
: ImmutableTuple<T> | ||
: {readonly [P in keyof T]: Immutable<T[P]>} | ||
: T | ||
|
||
export interface IProduce { | ||
/** | ||
* The `produce` function takes a value and a "recipe function" (whose | ||
|
@@ -92,32 +67,32 @@ export interface IProduce { | |
* @param {Function} patchListener - optional function that will be called with all the patches produced here | ||
* @returns {any} a new state, or the initial state if nothing was modified | ||
*/ | ||
<Base = any, Return = void>( | ||
base: Base extends Function ? never : Base, | ||
recipe: (this: Draft<Base>, draft: Draft<Base>) => Return, | ||
|
||
/** Curried producer */ | ||
< | ||
Recipe extends (...args: any[]) => any, | ||
Params extends any[] = Parameters<Recipe>, | ||
T = Params[0], | ||
>( | ||
recipe: Recipe, | ||
): (state: Immutable<T>, ...rest: Tail<Params>) => Produced<Immutable<T>, ReturnType<Recipe>> | ||
|
||
/** Curried producer with initial state */ | ||
< | ||
Recipe extends (...args: any[]) => any, | ||
Params extends any[] = Parameters<Recipe>, | ||
T = Params[0], | ||
>( | ||
recipe: Recipe, | ||
initialState: T | ||
): (state?: Immutable<T>, ...rest: Tail<Params>) => Produced<Immutable<T>, ReturnType<Recipe>> | ||
|
||
/** Normal producer */ | ||
<Base, D = Draft<Base>, Return = void>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Separating Base and D allows things like: |
||
base: Base, | ||
recipe: (this: D, draft: D) => Return, | ||
listener?: PatchListener | ||
): Produced<Base, Return> | ||
|
||
/** Curried producer with a default value */ | ||
<Base = any, Rest extends any[] = [], Return = void>( | ||
recipe: (this: Base, draft: Base, ...rest: Rest) => Return, | ||
defaultBase: Immutable<Base> | ||
): Rest[number][] extends Rest | never[] | ||
? ( | ||
// The `base` argument is optional when `Rest` is optional. | ||
base?: Immutable<Base>, | ||
...rest: Rest | ||
) => Produced<Immutable<Base>, Return> | ||
: ( | ||
// The `base` argument is required when `Rest` is required. | ||
base: Immutable<Base> | undefined, | ||
...rest: Rest | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in practice, this |
||
) => Produced<Immutable<Base>, Return> | ||
|
||
/** Curried producer with no default value */ | ||
<Base = any, Rest extends any[] = [], Return = void>( | ||
recipe: (this: Draft<Base>, draft: Draft<Base>, ...rest: Rest) => Return | ||
): (base: Immutable<Base>, ...rest: Rest) => Produced<Base, Return> | ||
} | ||
|
||
export const produce: IProduce | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doh, that should just be
State