UNPKG

4.7 kBTypeScriptView Raw
1import type {IsUnknown} from './is-unknown';
2import type {StaticPartOfArray, VariablePartOfArray} from './internal';
3import type {UnknownArray} from './unknown-array';
4
5/**
6Create 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
13type A = MergeObjectToArray<[string, number], {0: boolean}>;
14//=> [boolean, number]
15
16// array
17type B = MergeObjectToArray<[string, number], [boolean]>;
18//=> [boolean, number]
19
20// tailing spread array
21type C = MergeObjectToArray<[string, ...boolean[]], {1: number}>;
22//=> [string, ...number[]]
23
24type D = MergeObjectToArray<[string, ...boolean[]], [number, ...string[]]>;
25//=> [number, ...string[]]
26```
27*/
28type 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/**
56Create a function that replaces some parameters with the given parameters.
57
58The parameters that are not specified will be kept as-is.
59
60Note:
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
72Use-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```
79import type {SetParameterType} from 'type-fest';
80
81type HandleMessage = (data: Data, message: string, ...arguments: any[]) => void;
82
83type 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.
87type HandleError = SetParameterType<HandleMessage, [data: ErrorData, message: 'error']>;
88//=> type HandleError = (data: ErrorData, message: 'error') => void;
89
90// Change single parameter type.
91type 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.
97type 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.
101type HandleLog2 = SetParameterType<HandleMessage, {2: string}>;
102//=> type HandleLog2 = (data: Data, message: string, ...arguments: string[]) => void;
103```
104
105@category Function
106*/
107export 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