Skip to content

Commit

Permalink
SetParameterType: Fix compatibility with TypeScript 5.4 (#835)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
Emiyaaaaa and sindresorhus committed Mar 18, 2024
1 parent 4f14bff commit 2bc451e
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 18 deletions.
74 changes: 60 additions & 14 deletions source/set-parameter-type.d.ts
@@ -1,17 +1,53 @@
import type {IsUnknown} from './is-unknown';
import type {StaticPartOfArray} from './internal';
import type {UnknownArray} from './unknown-array';

/**
Create a tuple that replaces the given `Tuple`'s elements with the given `Record_`'s values at the given indices.
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 MergeObjectToTuple<Tuple extends unknown[], Record_ extends Record<number, unknown>> = {
[K in keyof Tuple]: Record_ extends Record<string, unknown>
// Handle object case like `{0: string, 1: number}`
? K extends `${infer NumberK extends number}`
? NumberK extends keyof Record_ ? Record_[K] : Tuple[K]
: never // Should not happen
// Handle tuple case like `[string, number]`
: K extends keyof Record_ ? Record_[K] : Tuple[K]
};
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]: 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.
Expand Down Expand Up @@ -39,7 +75,7 @@ Use-case:
```
import type {SetParameterType} from 'type-fest';

type HandleMessage = (data: Data, message: string) => void;
type HandleMessage = (data: Data, message: string, ...arguments: any[]) => void;

type HandleOk = SetParameterType<HandleMessage, {0: SuccessData, 1: 'ok'}>;
//=> type HandleOk = (data: SuccessData, message: 'ok') => void;
Expand All @@ -48,9 +84,19 @@ type HandleOk = SetParameterType<HandleMessage, {0: SuccessData, 1: 'ok'}>;
type HandleError = SetParameterType<HandleMessage, [data: ErrorData, message: 'error']>;
//=> type HandleError = (data: ErrorData, message: 'error') => void;

// Could change single parameter type.
// 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
Expand All @@ -62,7 +108,7 @@ export type SetParameterType<Fn extends (...arguments_: any[]) => unknown, P ext
// 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<ThisArg> extends true
? (...arguments_: MergeObjectToTuple<Arguments, P>) => ReturnType<Fn>
: (this: ThisArg, ...arguments_: MergeObjectToTuple<Arguments, P>) => ReturnType<Fn>
? (...arguments_: MergeObjectToArray<Arguments, P>) => ReturnType<Fn>
: (this: ThisArg, ...arguments_: MergeObjectToArray<Arguments, P>) => ReturnType<Fn>
)
: Fn; // This part should be unreachable
12 changes: 8 additions & 4 deletions test-d/set-parameter-type.ts
@@ -1,5 +1,5 @@
import {expectType} from 'tsd';
import {type SetParameterType} from '../index';
import type {SetParameterType} from '../index';

function fn(_a: number, _b: string, _c: Object, ..._arguments: boolean[]) {
return null;
Expand Down Expand Up @@ -27,9 +27,13 @@ expectType<(a: string, b: boolean, c: Object, ...args: boolean[]) => null>(test3
test3('1', true, {}, true);

// Test `...args` parameter
declare const test4: SetParameterType<typeof fn, {3: string}>;
expectType<(a: number, b: string, c: Object, ...args: string[]) => null>(test4);
test4(1, '1', {}, '1');
declare const testargs: SetParameterType<typeof fn, {3: string}>;
expectType<(a: number, b: string, c: Object, ...args: string[]) => null>(testargs);
testargs(1, '1', {}, '1');

declare const testargs2: SetParameterType<typeof fn, [string, number, number, ...boolean[]]>;
expectType<(a: string, b: number, c: number, ...args: boolean[]) => null>(testargs2);
testargs2('1', 1, 1, true);

// Test arrow function
declare const test5: SetParameterType<typeof arrowFunction, {0: string}>;
Expand Down

0 comments on commit 2bc451e

Please sign in to comment.