|
1 | 1 | import type {If} from '../if.d.ts'; |
2 | | -import type {IsAny} from '../is-any.d.ts'; |
3 | 2 | import type {IsNever} from '../is-never.d.ts'; |
| 3 | +import type {OptionalKeysOf} from '../optional-keys-of.d.ts'; |
4 | 4 | import type {UnknownArray} from '../unknown-array.d.ts'; |
5 | | -import type {IfNotAnyOrNever} from './type.d.ts'; |
| 5 | +import type {IsExactOptionalPropertyTypesEnabled, IfNotAnyOrNever} from './type.d.ts'; |
6 | 6 |
|
7 | 7 | /** |
8 | 8 | Infer the length of the given array `<T>`. |
@@ -97,37 +97,62 @@ Returns whether the given array `T` is readonly. |
97 | 97 | export type IsArrayReadonly<T extends UnknownArray> = If<IsNever<T>, false, T extends unknown[] ? false : true>; |
98 | 98 |
|
99 | 99 | /** |
100 | | -Returns a boolean for whether every element in an array type extends another type. |
101 | | -
|
102 | | -Note: |
103 | | -- This type is not designed to be used with non-tuple arrays (like `number[]`), tuples with optional elements (like `[1?, 2?, 3?]`), or tuples that contain a rest element (like `[1, 2, ...number[]]`). |
104 | | -- The `never` type does not match the target type unless the target type is `never` or `any`. For example, `Every<[never, never], never>` returns `true`, but `Every<[never, number], number>` returns `false`. |
| 100 | +Transforms a tuple type by replacing it's rest element with a single element that has the same type as the rest element, while keeping all the non-rest elements intact. |
105 | 101 |
|
106 | 102 | @example |
107 | 103 | ``` |
108 | | -import type {Every} from 'type-fest'; |
| 104 | +type A = CollapseRestElement<[string, string, ...number[]]>; |
| 105 | +//=> [string, string, number] |
| 106 | +
|
| 107 | +type B = CollapseRestElement<[...string[], number, number]>; |
| 108 | +//=> [string, number, number] |
109 | 109 |
|
110 | | -type A = Every<[1, 2, 3], number>; |
111 | | -//=> true |
| 110 | +type C = CollapseRestElement<[string, string, ...Array<number | bigint>]>; |
| 111 | +//=> [string, string, number | bigint] |
112 | 112 |
|
113 | | -type B = Every<[1, 2, '3'], number>; |
114 | | -//=> false |
| 113 | +type D = CollapseRestElement<[string, number]>; |
| 114 | +//=> [string, number] |
| 115 | +``` |
115 | 116 |
|
116 | | -type C = Every<[number, number | string], number>; |
117 | | -//=> boolean |
| 117 | +Note: Optional modifiers (`?`) are removed from elements unless the `exactOptionalPropertyTypes` compiler option is disabled. When disabled, there's an additional `| undefined` for optional elements. |
118 | 118 |
|
119 | | -type D = Every<[true, boolean, true], true>; |
120 | | -//=> boolean |
| 119 | +@example |
| 120 | +``` |
| 121 | +// `exactOptionalPropertyTypes` enabled |
| 122 | +type A = CollapseRestElement<[string?, string?, ...number[]]>; |
| 123 | +//=> [string, string, number] |
| 124 | +
|
| 125 | +// `exactOptionalPropertyTypes` disabled |
| 126 | +type B = CollapseRestElement<[string?, string?, ...number[]]>; |
| 127 | +//=> [string | undefined, string | undefined, number] |
121 | 128 | ``` |
122 | 129 | */ |
123 | | -export type Every<TArray extends UnknownArray, Type> = IfNotAnyOrNever<TArray, If<IsAny<Type>, true, |
124 | | - TArray extends readonly [infer First, ...infer Rest] |
125 | | - ? IsNever<First> extends true |
126 | | - ? IsNever<Type> extends true |
127 | | - ? Every<Rest, Type> |
128 | | - : false |
129 | | - : First extends Type |
130 | | - ? Every<Rest, Type> |
131 | | - : false |
132 | | - : true |
133 | | ->, false, false>; |
| 130 | +export type CollapseRestElement<TArray extends UnknownArray> = IfNotAnyOrNever<TArray, _CollapseRestElement<TArray>>; |
| 131 | + |
| 132 | +type _CollapseRestElement< |
| 133 | + TArray extends UnknownArray, |
| 134 | + ForwardAccumulator extends UnknownArray = [], |
| 135 | + BackwardAccumulator extends UnknownArray = [], |
| 136 | +> = |
| 137 | + TArray extends UnknownArray // For distributing `TArray` |
| 138 | + ? keyof TArray & `${number}` extends never |
| 139 | + // Enters this branch, if `TArray` is empty (e.g., []), |
| 140 | + // or `TArray` contains no non-rest elements preceding the rest element (e.g., `[...string[]]` or `[...string[], string]`). |
| 141 | + ? TArray extends readonly [...infer Rest, infer Last] |
| 142 | + ? _CollapseRestElement<Rest, ForwardAccumulator, [Last, ...BackwardAccumulator]> // Accumulate elements that are present after the rest element. |
| 143 | + : TArray extends readonly [] |
| 144 | + ? [...ForwardAccumulator, ...BackwardAccumulator] |
| 145 | + : [...ForwardAccumulator, TArray[number], ...BackwardAccumulator] // Add the rest element between the accumulated elements. |
| 146 | + : TArray extends readonly [(infer First)?, ...infer Rest] |
| 147 | + ? _CollapseRestElement< |
| 148 | + Rest, |
| 149 | + [ |
| 150 | + ...ForwardAccumulator, |
| 151 | + '0' extends OptionalKeysOf<TArray> |
| 152 | + ? If<IsExactOptionalPropertyTypesEnabled, First, First | undefined> // Add `| undefined` for optional elements, if `exactOptionalPropertyTypes` is disabled. |
| 153 | + : First, |
| 154 | + ], |
| 155 | + BackwardAccumulator |
| 156 | + > |
| 157 | + : never // Should never happen, since `[(infer First)?, ...infer Rest]` is a top-type for arrays. |
| 158 | + : never; // Should never happen |
0 commit comments