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