-
-
Notifications
You must be signed in to change notification settings - Fork 547
/
set-parameter-type.d.ts
117 lines (97 loc) · 4.59 KB
/
set-parameter-type.d.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import type {IsUnknown} from './is-unknown';
import type {StaticPartOfArray, VariablePartOfArray} from './internal';
import type {UnknownArray} from './unknown-array';
/**
Create an array that replaces the given `TArray`'s elements with the given `TObject`'s values at the given indices.
`TArray` and `TObject` supports tailing spread array like `[string, ...boolean[]]`, but does not support `[string, ...boolean[], number]`.
@example:
```ts
// object
type A = MergeObjectToArray<[string, number], {0: boolean}>;
//=> [boolean, number]
// array
type B = MergeObjectToArray<[string, number], [boolean]>;
//=> [boolean, number]
// tailing spread array
type C = MergeObjectToArray<[string, ...boolean[]], {1: number}>;
//=> [string, ...number[]]
type D = MergeObjectToArray<[string, ...boolean[]], [number, ...string[]]>;
//=> [number, ...string[]]
```
*/
type MergeObjectToArray<TArray extends UnknownArray, TObject, TArrayCopy extends UnknownArray = TArray> =
// If `TObject` is an array like `[0, 1, 2]`
TObject extends UnknownArray
// If `TObject` is a variable length array, we should use `TObject`'s type as the result type.
? number extends TObject['length']
? TObject
: {
[K in keyof TArray]:
number extends K
? VariablePartOfArray<TArray>[number]
: K extends keyof TObject ? TObject[K] : TArray[K]
}
: TObject extends object
// If `TObject` is a object witch key is number like `{0: string, 1: number}`
? {
[K in keyof TArray]:
K extends `${infer NumberK extends number}`
? NumberK extends keyof TObject ? TObject[NumberK] : TArray[K]
: number extends K
// If array key `K` is `number`, means it's a rest parameter, we should set the rest parameter type to corresponding type in `TObject`.
// example: `MergeObjectToParamterArray<[string, ...boolean[]], {1: number}>` => `[string, ...number[]]`
? StaticPartOfArray<TArrayCopy>['length'] extends keyof TObject
? TObject[StaticPartOfArray<TArrayCopy>['length']]
: TArray[K]
: never
} : never;
/**
Create a function that replaces some parameters with the given parameters.
The parameters that are not specified will be kept as-is.
Note:
- This type will ignore the given function's generic type.
- If you change the parameter type that return type depends on, the return type will not change:
```
const fn = (a: number) => a;
//=> fn: (a: number) => number;
// We change type of `a` to `string`, but return type is still `number`.
type Fn = SetParameterType<typeof fn, {0: string}>;
//=> (a: string) => number;
```
Use-case:
- Define a wrapped function that receives something different while returning the same type.
- Mocking and testing.
- Overload function type. (See example)
@example
```
import type {SetParameterType} from 'type-fest';
type HandleMessage = (data: Data, message: string, ...arguments: any[]) => void;
type HandleOk = SetParameterType<HandleMessage, {0: SuccessData, 1: 'ok'}>;
//=> type HandleOk = (data: SuccessData, message: 'ok') => void;
// Another way to define the parameters to replace.
type HandleError = SetParameterType<HandleMessage, [data: ErrorData, message: 'error']>;
//=> type HandleError = (data: ErrorData, message: 'error') => void;
// Change single parameter type.
type HandleWarn = SetParameterType<HandleMessage, {1: 'warn'}>;
//=> type HandleWarn = (data: Data, message: 'warn') => void;
// Change rest parameter type.
// Way 1: Input full parameter type.
type HandleLog = SetParameterType<HandleMessage, [data: Data, message: 'log', ...arguments: string[]]>;
//=> type HandleLog = (data: Data, message: 'log', ...arguments: string[]) => void;
// Way 2: Input rest parameter type by Object index.
type HandleLog2 = SetParameterType<HandleMessage, {2: string}>;
//=> type HandleLog2 = (data: Data, message: string, ...arguments: string[]) => void;
```
@category Function
*/
export type SetParameterType<Function_ extends (...arguments_: any[]) => unknown, P extends Record<number, unknown>> =
// Just using `Parameters<Fn>` isn't ideal because it doesn't handle the `this` fake parameter.
Function_ extends (this: infer ThisArgument, ...arguments_: infer Arguments) => unknown
? (
// If a function did not specify the `this` fake parameter, it will be inferred to `unknown`.
// We want to detect this situation just to display a friendlier type upon hovering on an IntelliSense-powered IDE.
IsUnknown<ThisArgument> extends true
? (...arguments_: MergeObjectToArray<Arguments, P>) => ReturnType<Function_>
: (this: ThisArgument, ...arguments_: MergeObjectToArray<Arguments, P>) => ReturnType<Function_>
)
: Function_; // This part should be unreachable