diff --git a/index.d.ts b/index.d.ts index ee07d85a8..43e3ffa86 100644 --- a/index.d.ts +++ b/index.d.ts @@ -107,6 +107,7 @@ export type {HasReadonlyKeys} from './source/has-readonly-keys.d.ts'; export type {WritableKeysOf} from './source/writable-keys-of.d.ts'; export type {HasWritableKeys} from './source/has-writable-keys.d.ts'; export type {Spread} from './source/spread.d.ts'; +export type {SplitOnSpread, ExcludeSpread, ExtractSpread} from './source/split-on-spread.js'; export type {IsInteger} from './source/is-integer.d.ts'; export type {IsFloat} from './source/is-float.d.ts'; export type {TupleToObject} from './source/tuple-to-object.d.ts'; @@ -168,12 +169,16 @@ export type {DelimiterCase} from './source/delimiter-case.d.ts'; export type {DelimiterCasedProperties} from './source/delimiter-cased-properties.d.ts'; export type {DelimiterCasedPropertiesDeep} from './source/delimiter-cased-properties-deep.d.ts'; export type {Join} from './source/join.d.ts'; +export type {Reverse} from './source/reverse.d.ts'; export type {Split} from './source/split.d.ts'; export type {Words} from './source/words.d.ts'; export type {Trim} from './source/trim.d.ts'; export type {Replace} from './source/replace.d.ts'; export type {StringRepeat} from './source/string-repeat.d.ts'; export type {Includes} from './source/includes.d.ts'; +export type {IndexOf, LastIndexOf} from './source/index-of.d.ts'; +export type {IndicesOf} from './source/indices-of.d.ts'; +export type {CountOf} from './source/count-of.d.ts'; export type {Get} from './source/get.d.ts'; export type {LastArrayElement} from './source/last-array-element.d.ts'; export type {ConditionalSimplify} from './source/conditional-simplify.d.ts'; @@ -185,5 +190,6 @@ export type {PackageJson} from './source/package-json.d.ts'; export type {TsConfigJson} from './source/tsconfig-json.d.ts'; // Improved built-in +export type {ExtendsStrict} from './source/extends-strict.d.ts'; export type {ExtractStrict} from './source/extract-strict.d.ts'; export type {ExcludeStrict} from './source/exclude-strict.d.ts'; diff --git a/readme.md b/readme.md index 3f04fef88..d586e40d9 100644 --- a/readme.md +++ b/readme.md @@ -161,14 +161,13 @@ Click the type names for complete docs. - [`WritableKeysOf`](source/writable-keys-of.d.ts) - Extract all writable (non-readonly) keys from the given type. - [`HasWritableKeys`](source/has-writable-keys.d.ts) - Create a `true`/`false` type depending on whether the given type has any writable fields. - [`Spread`](source/spread.d.ts) - Mimic the type inferred by TypeScript when merging two objects or two arrays/tuples using the spread syntax. +- [`SplitOnSpread`](source/split-on-spread.d.ts) - Splits an Array on its spreaded portion. return's `[[...Head], [Spread], [...Tail]]`. +- [`ExtractSpread`](source/split-on-spread.d.ts) - Return's the spread element type from and array. +- [`ExcludeSpread`](source/split-on-spread.d.ts) - Create a array with the spread element removed. - [`IsEqual`](source/is-equal.d.ts) - Returns a boolean for whether the two given types are equal. - [`TaggedUnion`](source/tagged-union.d.ts) - Create a union of types that share a common discriminant property. - [`IntRange`](source/int-range.d.ts) - Generate a union of numbers (includes the start and excludes the end). - [`IntClosedRange`](source/int-closed-range.d.ts) - Generate a union of numbers (includes the start and the end). -- [`ArrayIndices`](source/array-indices.d.ts) - Provides valid indices for a constant array or tuple. -- [`ArrayValues`](source/array-values.d.ts) - Provides all values for a constant array or tuple. -- [`ArraySplice`](source/array-splice.d.ts) - Creates a new array type by adding or removing elements at a specified index range in the original array. -- [`ArrayTail`](source/array-tail.d.ts) - Extracts the type of an array or tuple minus the first element. - [`SetFieldType`](source/set-field-type.d.ts) - Create a type that changes the type of the given keys. - [`Paths`](source/paths.d.ts) - Generate a union of all possible paths to properties in the given object. - [`SharedUnionFields`](source/shared-union-fields.d.ts) - Create a type with shared fields from a union of object types. @@ -236,7 +235,16 @@ Click the type names for complete docs. - [`Arrayable`](source/arrayable.d.ts) - Create a type that represents either the value or an array of the value. - [`Includes`](source/includes.d.ts) - Returns a boolean for whether the given array includes the given item. - [`Join`](source/join.d.ts) - Join an array of strings and/or numbers using the given string as a delimiter. +- [`Reverse`](source/array-reverse.d.ts) - Creates a new array type by Reversing the order of each element in the original array. +- [`IndexOf`](source/index-of.d.ts) - Returns the index of the first occurrence of a value in an array, or `-1` if it is not present. +- [`LastIndexOf`](source/index-of.d.ts) - Returns the index of the last occurrence of a value in an array, or `-1` if it is not present. +- [`IndicesOf`](source/index-of.d.ts) - Returns All the indices of the occurrence of a value in an array, or `[]` if it is not present. +- [`CountOf`](source/count-of.d.ts) - Returns the count of occurrences of a value in an array, or `0` if it is not present. - [`ArraySlice`](source/array-slice.d.ts) - Returns an array slice of a given range, just like `Array#slice()`. +- [`ArrayIndices`](source/array-indices.d.ts) - Provides valid indices for a constant array or tuple. +- [`ArrayValues`](source/array-values.d.ts) - Provides all values for a constant array or tuple. +- [`ArraySplice`](source/array-splice.d.ts) - Creates a new array type by adding or removing elements at a specified index range in the original array. +- [`ArrayTail`](source/array-tail.d.ts) - Extracts the type of an array or tuple minus the first element. - [`LastArrayElement`](source/last-array-element.d.ts) - Extracts the type of the last element of an array. - [`FixedLengthArray`](source/fixed-length-array.d.ts) - Create a type that represents an array of the given type and length. - [`MultidimensionalArray`](source/multidimensional-array.d.ts) - Create a type that represents a multidimensional array of the given type and dimensions. @@ -295,6 +303,7 @@ Click the type names for complete docs. ### Improved built-in +- [`ExtendsStrict`](source/extends-strict.d.ts) - A stricter version of `extends` that Checks Stricily if one type extends another without distribution. - [`ExtractStrict`](source/extract-strict.d.ts) - A stricter version of `Extract` that ensures every member of `U` can successfully extract something from `T`. - [`ExcludeStrict`](source/exclude-strict.d.ts) - A stricter version of `Exclude` that ensures every member of `U` can successfully exclude something from `T`. diff --git a/source/array-splice.d.ts b/source/array-splice.d.ts index 616eab975..08f5f6df9 100644 --- a/source/array-splice.d.ts +++ b/source/array-splice.d.ts @@ -55,7 +55,7 @@ type B = SplitArrayByIndex<[1, 2, 3, 4], 0>; // type B = [[], [1, 2, 3, 4]]; ``` */ -type SplitArrayByIndex = +export type SplitArrayByIndex = SplitIndex extends 0 ? [[], T] : number extends T['length'] diff --git a/source/count-of.d.ts b/source/count-of.d.ts new file mode 100644 index 000000000..a2e590e75 --- /dev/null +++ b/source/count-of.d.ts @@ -0,0 +1,24 @@ +import type {UnknownArray} from './unknown-array.d.ts'; +import type {IndicesOf} from './indices-of.d.ts'; + +/** +Returns the count of occurrences of a value in an array, or `0` if it is not present. + +@example +``` +type T = CountOf<[1, 2, 1, 1], 1>; +//=> 3 + +declare function getCount(array: T, item: I): CountOf; +getCount(['a', 'b', 'a'], 'a'); +//=> 2 instead of `number` +``` + +@author benzaria +@see Includes +@category Array +*/ +export type CountOf = + IndicesOf extends infer Indices extends number[] + ? Indices['length'] + : 0; diff --git a/source/extends-strict.d.ts b/source/extends-strict.d.ts new file mode 100644 index 000000000..1897e915b --- /dev/null +++ b/source/extends-strict.d.ts @@ -0,0 +1,34 @@ +import type {IsNever} from './is-never.d.ts'; + +/** +A stricter version of `extends` that Checks Stricily if one type extends another without distribution. + +Note: this is not quite the same as `Left extends Right` because: + +1. Types are wrapped in a 1-tuple so that union types are not distributed - instead we consider `string | number` to _not_ extend `number`. If we used `Left extends Right` directly you would get `Extends` => `false | true` => `boolean`. So it's return `true` if `[Left] extends [Right]`. + +2. Return's `true` if `Left` and `Right` are both `never`. + +@example +``` +type T = ExtendsStrict +//=> false + +type T = ExtendsStrict +//=> true + +type T = ExtendsStrict +//=> true + +type T = ExtendsStrict +//=> true +``` + +@category Improved Built-in +*/ +export type ExtendsStrict = + IsNever extends true + ? IsNever + : [Left] extends [Right] + ? true + : false; diff --git a/source/includes.d.ts b/source/includes.d.ts index 39dbd6721..a7c2a8d30 100644 --- a/source/includes.d.ts +++ b/source/includes.d.ts @@ -1,4 +1,5 @@ -import type {IsEqual} from './is-equal.d.ts'; +import type {UnknownArray} from './unknown-array.d.ts'; +import type {IndexOf} from './index-of.d.ts'; /** Returns a boolean for whether the given array includes the given item. @@ -12,11 +13,8 @@ import type {Includes} from 'type-fest'; type hasRed = Includes; ``` +@see CountOf @category Array */ -export type Includes = - Value extends readonly [Value[0], ...infer rest] - ? IsEqual extends true - ? true - : Includes - : false; +export type Includes = + IndexOf extends -1 ? false : true; diff --git a/source/index-of.d.ts b/source/index-of.d.ts new file mode 100644 index 000000000..ab73b2609 --- /dev/null +++ b/source/index-of.d.ts @@ -0,0 +1,79 @@ +import type {SplitArrayByIndex} from './array-splice.d.ts'; +import type {SubtractPositives} from './subtract.d.ts'; +import type {UnknownArray} from './unknown-array.d.ts'; +import type {SumPositives} from './sum.d.ts'; +import type {IsEqual} from './is-equal.d.ts'; +import type {Reverse} from './reverse.d.ts'; + +/** +Simpler version of Sum, without the extra logic. +*/ +export type Increment = SumPositives; + +type _IndexOf< + Array_ extends UnknownArray, Item, + FromIndex extends number = 0, + Index extends number = 0, +> = ( + Array_ extends readonly [infer Head, ...infer Tail] + ? IsEqual extends true + ? SumPositives + : _IndexOf> + : -1 // Same as `indexOf` +); + +/** +Returns the index of the first occurrence of a value in an array, or `-1` if it is not present. + +@example +``` +type T = IndexOf; +//=> 1 + +type T = IndexOf<[1, 2, 3], 4>; +//=> -1 + +type T = IndexOf<[{a: 1}, {a: 1}, {b: 1}], {a: 1}>; +//=> 0 +``` + +@author benzaria +@see LastIndexOf, IndicesOf +@category Array +*/ +// TODO: Add `ToIndex` parameter +export type IndexOf< + Array_ extends UnknownArray, Item, + FromIndex extends number = 0, +> = _IndexOf[1], Item, FromIndex>; +// Return's never If `FromIndex > ArrayLength` + +/** +Returns the index of the last occurrence of a value in an array, or `-1` if it is not present. + +@example +``` +type T = LastIndexOf; +//=> 2 + +type T = LastIndexOf<[1, 2, 3], 4>; +//=> -1 + +type T = LastIndexOf<[{a: 1}, {a: 1}, {b: 1}], {a: 1}>; +//=> 1 +``` + +@author benzaria +@see IndexOf, IndiciesOf +@category Array +*/ +export type LastIndexOf< + Array_ extends UnknownArray, Item, + FromIndex extends number = 0, +> = ( + IndexOf, Item, FromIndex> extends infer Index extends number + ? Index extends -1 + ? -1 + : SubtractPositives> + : never +); diff --git a/source/indices-of.d.ts b/source/indices-of.d.ts new file mode 100644 index 000000000..16e7036c0 --- /dev/null +++ b/source/indices-of.d.ts @@ -0,0 +1,41 @@ +import type {Increment, IndexOf} from './index-of.d.ts'; +import type {UnknownArray} from './unknown-array.d.ts'; + +/** +Search for an item in a array and return it's indices. +*/ +type _IndicesOf< + Array_ extends UnknownArray, Item, + FromIndex extends number = 0, + Indices extends number[] = [], +> = ( + IndexOf extends infer Index extends number + ? Index extends -1 + ? Indices + : _IndicesOf, [...Indices, Index]> + : never +); + +/** +Returns the index of the first occurrence of a value in an array, or `-1` if it is not present. + +@example +``` +type T = IndicesOf; +//=> [1, 2] + +type T = IndicesOf<[1, 2, 3], 4>; +//=> [] + +type T = IndicesOf<[{a: 1}, {a: 1}, {b: 1}], {a: 1}>; +//=> [0, 1] +``` + +@author benzaria +@see IndexOf, LastIndexOf +@category Array +*/ +export type IndicesOf< + Array_ extends UnknownArray, Item, + FromIndex extends number = 0, +> = _IndicesOf; diff --git a/source/internal/array.d.ts b/source/internal/array.d.ts index 8907cbdf5..8cc0986da 100644 --- a/source/internal/array.d.ts +++ b/source/internal/array.d.ts @@ -1,7 +1,7 @@ -import type {If} from '../if.d.ts'; -import type {IsAny} from '../is-any.d.ts'; -import type {IsNever} from '../is-never.d.ts'; import type {UnknownArray} from '../unknown-array.d.ts'; +import type {IsNever} from '../is-never.d.ts'; +import type {IsAny} from '../is-any.d.ts'; +import type {If} from '../if.d.ts'; import type {IfNotAnyOrNever} from './type.d.ts'; /** @@ -45,8 +45,8 @@ type B = StaticPartOfArray; */ export type StaticPartOfArray = T extends unknown - ? number extends T['length'] ? - T extends readonly [infer U, ...infer V] + ? number extends T['length'] + ? T extends readonly [infer U, ...infer V] ? StaticPartOfArray : Result : T diff --git a/source/internal/type.d.ts b/source/internal/type.d.ts index 85dac83ff..fee1d5e61 100644 --- a/source/internal/type.d.ts +++ b/source/internal/type.d.ts @@ -1,7 +1,9 @@ -import type {If} from '../if.d.ts'; -import type {IsAny} from '../is-any.d.ts'; -import type {IsNever} from '../is-never.d.ts'; +import type {ExtendsStrict} from '../extends-strict.d.ts'; import type {Primitive} from '../primitive.d.ts'; +import type {IsNever} from '../is-never.d.ts'; +import type {IsAny} from '../is-any.d.ts'; +import type {Or} from '../or.d.ts'; +import type {If} from '../if.d.ts'; /** Matches any primitive, `void`, `Date`, or `RegExp` value. @@ -40,9 +42,19 @@ export type HasMultipleCallSignatures unknow : false; /** -Returns a boolean for whether the given `boolean` is not `false`. +Returns a boolean for whether the given `boolean` Union contain `false`. */ -export type IsNotFalse = [T] extends [false] ? false : true; +export type IsNotFalse = Not>; + +/** +Returns a boolean for whether the given `boolean` Union members are all `true`. +*/ +export type IsTrue = ExtendsStrict; + +/** +Returns a boolean for whether the given `boolean` Union members are all `false`. +*/ +export type IsFalse = ExtendsStrict; /** Returns a boolean for whether the given type is primitive value or primitive type. @@ -59,7 +71,7 @@ IsPrimitive //=> false ``` */ -export type IsPrimitive = [T] extends [Primitive] ? true : false; +export type IsPrimitive = ExtendsStrict; /** Returns a boolean for whether A is false. @@ -99,3 +111,8 @@ type C = IfNotAnyOrNever; */ export type IfNotAnyOrNever = If, IfAny, If, IfNever, IfNotAnyOrNever>>; + +/** +Determines if a type is either `never` or `any`. +*/ +export type IsAnyOrNever = Or, IsNever>; diff --git a/source/reverse.d.ts b/source/reverse.d.ts new file mode 100644 index 000000000..56acc2046 --- /dev/null +++ b/source/reverse.d.ts @@ -0,0 +1,132 @@ +import type {ApplyDefaultOptions} from './internal/object.d.ts'; +import type {IsArrayReadonly} from './internal/array.d.ts'; +import type {SplitOnSpread} from './split-on-spread.d.ts'; +import type {ExtendsStrict} from './extends-strict.d.ts'; +import type {UnknownArray} from './unknown-array.d.ts'; +import type {ArrayTail} from './array-tail.d.ts'; +import type {IsAny} from './is-any.d.ts'; +import type {And} from './and.d.ts'; +import type {Or} from './or.d.ts'; + +/** +@see {@link Reverse} +*/ +type ArrayReverseOptions = { + /** + Return a reversed readonly array if the input array is readonly. + + @default true + + @example + ``` + import type {Reverse} from 'type-fest'; + + type Example1 = Reverse; + //=> readonly [boolean, number, string] + + type Example2 = Reverse<[string, number, boolean], {preserveReadonly: true}>; + //=> [boolean, number, string] + + type Example3 = Reverse; + //=> [boolean, number, string] + + type Example4 = Reverse<[string, number, boolean], {preserveReadonly: false}>; + //=> [boolean, number, string] + ``` + */ + preserveReadonly?: boolean; + /** + Return a reversed array with Optional keys as `type | undefined`. + + @default false + + @example + ``` + import type {Reverse} from 'type-fest'; + + type Example1 = Reverse<[string, number, boolean?], {keepOptional: false}>; + //=> [boolean, number, string] + + type Example2 = Reverse<[string, number, boolean?], {keepOptional: true}>; + //=> [boolean | undefined, number, string] + + type Example3 = Reverse<[string, number, boolean], {keepOptional: false}>; + //=> [boolean, number, string] + + type Example4 = Reverse<[string, number, boolean], {keepOptional: true}>; + //=> [boolean, number, string] + ``` + */ + keepOptionals?: boolean; +}; + +type DefaultArrayReverseOptions = { + preserveReadonly: true; + keepOptionals: false; +}; + +/** +Reverse an Array Items. +*/ +type _Reverse, AHead = Array_[0]> = + Array_ extends readonly [] + ? [] + : [ + ..._Reverse, Options>, + Or< + Options['keepOptionals'], + ExtendsStrict + > extends true + ? AHead + : Exclude, + ]; + +/** +Creates a new array type by Reversing the order of each element in the original array. + +By default, The type Preserve `readonly` modifier, and replace Optional keys with `type | undefined`. See {@link ArrayReverseOptions} options to change this behaviour. + +@example +``` +type T = Reverse<['a', 'b', 'c']> +//=> ['c', 'b', 'a'] + +type T = Reverse +//=> readonly [6, 5, 4, 3, 2, 1] + +type T = Reverse<['a', 'b'?, 'c'?]> +//=> ['c', 'b', 'a'] + +type T = Reverse +//=> readonly [...number[], 2, 1] + +declare function reverse(array: T): Reverse; +reverse(['a', 'b', 'c', 'd']); +//=> ['d', 'c', 'b', 'a'] +``` + +@author benzaria +@see {@link ArrayReverseOptions} +@category Array +*/ +export type Reverse = + ApplyDefaultOptions extends infer ResolvedOptions extends Required + ? IsAny extends false // Prevent the return of `Readonly<[] | [unknown] | unknown[] | [...unknown[], unknown]>` + ? Array_ extends UnknownArray // For distributing `Array_` + ? SplitOnSpread extends infer _Result extends UnknownArray[] + ? [ + ..._Reverse<_Result[2], ResolvedOptions>, + ..._Result[1], + ..._Reverse<_Result[0], ResolvedOptions>, + ] extends infer Result + ? And< + ResolvedOptions['preserveReadonly'], + IsArrayReadonly + > extends true + ? Readonly + : Result + : never + : never + : never + : never + : never; diff --git a/source/split-on-spread.d.ts b/source/split-on-spread.d.ts new file mode 100644 index 000000000..6a2f3b7d5 --- /dev/null +++ b/source/split-on-spread.d.ts @@ -0,0 +1,90 @@ +import type {StaticPartOfArray, VariablePartOfArray} from './internal/array.d.ts'; +import type {UnknownArray} from './unknown-array.d.ts'; + +/** +Return's the spread element type from and array. + +@example +``` +type T = ExtractSpread<[number, ...string[], string, 'foo']>; +// => string[] + +type U = ExtractSpread<[...boolean[], string]>; +// => boolean[] + +type V = ExtractSpread<[number, string]>; +// => never +``` + +@author benzaria +@see ExcludeSpread, SplitOnSpread +@category Array +*/ +export type ExtractSpread = + SplitOnSpread[1] extends infer Result extends UnknownArray + ? Result extends [] + ? never + : Result[0] + : never; + +/** +Create a tuple with the spread element removed. + +@example +``` +type T = ExcludeSpread<[number, ...string[], string, 'foo']>; +// => [number, string, 'foo'] + +type U = ExcludeSpread<[...boolean[], string]>; +// => [string] + +type V = ExcludeSpread<[number, string]>; +// => [number, string] +``` + +@author benzaria +@see ExtractSpread, SplitOnSpread +@category Array +*/ +export type ExcludeSpread = + SplitOnSpread extends infer Result extends UnknownArray[] + ? [...Result[0], ...Result[2]] + : never; + +/** +Splits an Array on its spreaded portion. + +- The first part is the static head before the spread. +- The second part is a single-element tuple containing the spread type (e.g., `boolean` for `...boolean[]`). +- The third part is the static tail after the spread. + +If no spread is present, it treats the entire array as static. + +@example +``` +type T = SplitOnSpread<[number, ...string[], boolean]>; +// => [[number], [string], [boolean]] + +type U = SplitOnSpread<[...boolean[], string]>; +// => [[], [boolean], [string]] + +type V = SplitOnSpread<[number, string]>; +// => [[number, string], [], []] +``` + +@author benzaria +@see ExtractSpread, ExcludeSpread +@category Array +*/ +export type SplitOnSpread = [ + StaticPartOfArray, + ..._SplitOnSpread>, +]; + +/** +Splits an Array on its spreaded portion, When It's the first element. +*/ +type _SplitOnSpread = + T extends readonly [...infer Spread, infer Last] + ? _SplitOnSpread + : [T, Rest]; diff --git a/source/subtract.d.ts b/source/subtract.d.ts index 5e88a2fd5..443e2b1b8 100644 --- a/source/subtract.d.ts +++ b/source/subtract.d.ts @@ -67,7 +67,7 @@ type SubtractPostChecks = +export type SubtractPositives = LessThan extends true // When A < B we can reverse the result of B - A ? ReverseSign> diff --git a/source/sum.d.ts b/source/sum.d.ts index d9eaa15e0..1a27b37ee 100644 --- a/source/sum.d.ts +++ b/source/sum.d.ts @@ -72,7 +72,7 @@ type SumPostChecks = +export type SumPositives = [...BuildTuple, ...BuildTuple]['length'] extends infer Result extends number ? Result : never; diff --git a/test-d/count-of.ts b/test-d/count-of.ts new file mode 100644 index 000000000..aee39483d --- /dev/null +++ b/test-d/count-of.ts @@ -0,0 +1,26 @@ +import {expectType} from 'tsd'; +import type {CountOf} from '../source/count-of.d.ts'; + +// Base case +expectType>(0); +expectType>(1); +expectType>(3); +expectType>(2); +expectType>(2); + +expectType>(1); +expectType>(1); + +// Readonly +expectType>(2); +expectType>(1); + +// Union and edge types +expectType>(1); +expectType>(1); +expectType>(1); +expectType>(2); + +declare function getCount(array: T, item: I): CountOf; + +expectType<2>(getCount(['a', 'b', 'a'], 'a')); diff --git a/test-d/extends-strict.ts b/test-d/extends-strict.ts new file mode 100644 index 000000000..d92ae5049 --- /dev/null +++ b/test-d/extends-strict.ts @@ -0,0 +1,20 @@ +import {expectType} from 'tsd'; +import type {ExtendsStrict} from '../source/extends-strict.d.ts'; + +expectType>(false); +expectType>(true); +expectType>(true); +expectType>(true); + +expectType>(false); +expectType>(false); +expectType>(true); +expectType>(false); +expectType>(false); +expectType>(true); + +expectType>(false); +expectType>(true); +expectType>(false); +expectType>(true); +expectType>(false); diff --git a/test-d/index-of.ts b/test-d/index-of.ts new file mode 100644 index 000000000..8a19a824f --- /dev/null +++ b/test-d/index-of.ts @@ -0,0 +1,50 @@ +import {expectType} from 'tsd'; +import type {IndexOf, LastIndexOf} from '../index.d.ts'; + +// IndexOf + +// Basic cases +expectType>(-1); +expectType>(0); +expectType>(1); +expectType>(-1); + +expectType>(0); +expectType>(1); + +// Duplicate values +expectType>(0); +expectType>(0); + +// Readonly and tuple +expectType>(1); +expectType>(1); + +// Edge: optional/union types +expectType>(-1); +expectType>(0); +expectType>(1); + +// LastIndexOf + +// Basic cases +expectType>(-1); +expectType>(0); +expectType>(1); +expectType>(-1); + +expectType>(1); +expectType>(2); + +// Duplicate values +expectType>(2); +expectType>(2); + +// Readonly and tuple +expectType>(1); +expectType>(2); + +// Edge: optional/union types +expectType>(2); +expectType>(0); +expectType>(3); diff --git a/test-d/indices-of.ts b/test-d/indices-of.ts new file mode 100644 index 000000000..aaae9c87b --- /dev/null +++ b/test-d/indices-of.ts @@ -0,0 +1,26 @@ +import {expectType} from 'tsd'; +import type {IndicesOf} from '../index.d.ts'; + +// Base case +expectType>([]); +expectType>([1]); +expectType>([0, 2, 3]); +expectType>([0, 2]); +expectType>([1, 2]); + +expectType>([0]); +expectType>([1]); + +// Readonly +expectType>([1, 2]); +expectType>([1]); + +// Union and edge types +expectType>([1]); +expectType>([1]); +expectType>([0]); +expectType>([1, 3]); + +declare function getIndices(array: T, item: I): IndicesOf; + +expectType<[0, 2]>(getIndices(['a', 'b', 'a'], 'a')); diff --git a/test-d/internal/split-on-spread.ts b/test-d/internal/split-on-spread.ts new file mode 100644 index 000000000..2c5da3948 --- /dev/null +++ b/test-d/internal/split-on-spread.ts @@ -0,0 +1,54 @@ +import {expectType} from 'tsd'; +import type {ExcludeSpread, ExtractSpread, SplitOnSpread} from '../../source/split-on-spread.d.ts'; + +// Fixed Tuples +expectType>({} as [[], [], []]); +expectType>({} as [[1], [], []]); +expectType>({} as [[1, 2, 3], [], []]); +expectType>({} as [readonly ['a', 'b', 'c'], [], []]); + +// Finite Spread +expectType>({} as [[number, number, string], [], []]); +expectType>({} as [[], number[], [string[]]]); +expectType>({} as [[string?, number?], [], []]); +expectType>({} as [[never, 1], [], []]); +expectType>({} as [[1], [], []]); + +// Infinite Spread +expectType>({} as [[1], number[], []]); +expectType>({} as [['start'], string[], []]); +expectType>({} as [[1], number[], [2]]); +expectType>({} as [['start'], string[], ['end']]); +expectType>({} as [[], string[], []]); +expectType>({} as [[], number[], []]); +expectType>({} as [[], any[], []]); +expectType>({} as [[], unknown[], []]); +expectType>({} as [[], any[], []]); +expectType>({} as [[], number[], [2]]); +expectType>({} as [[], string[], ['end']]); +expectType>({} as [[], string[], []]); +expectType>({} as [[], never[], [1]]); +expectType, unknown]>>({} as [[], Array<[number, number | string]>, [unknown]]); + +// Edge cases +expectType>({} as [readonly [], [], []]); +expectType>({} as [readonly [number], [], []]); +expectType>({} as [[1], [], []]); + +// Readonly +expectType>({} as [readonly [1, 2, 3], [], []]); +expectType>({} as [[], number[], [2]]); + +// ExtractSpread +expectType>({} as string); +expectType>({} as number); +expectType>({} as boolean); +expectType, unknown]>>({} as 'foo'); +expectType]>>({} as 5); + +// ExcludeSpread +expectType>({} as ['start']); +expectType>({} as ['foo', true]); +expectType>({} as ['foo', 'bar']); +expectType, unknown]>>({} as [unknown]); +expectType]>>({} as ['bar']); diff --git a/test-d/reverse.ts b/test-d/reverse.ts new file mode 100644 index 000000000..3c145ca11 --- /dev/null +++ b/test-d/reverse.ts @@ -0,0 +1,93 @@ +import {expectType} from 'tsd'; +import type {Reverse} from '../index.d.ts'; + +declare const never: never; +declare const string: string; +declare const number: number; +declare const symbol: symbol; +declare const boolean: boolean; + +// Edge cases +expectType>([]); +expectType>(never); +expectType>(never); + +// Single element +expectType>([1] as const); +expectType>(['a'] as const); +expectType>([undefined] as const); +expectType>([null] as const); +expectType>([boolean] as const); +expectType>([true] as const); +expectType>([false] as const); +expectType>([symbol] as const); +expectType>([0n] as const); + +// Two and three elements +expectType>([2, 1] as const); +expectType>(['a', 1] as const); +expectType>([false, true] as const); +expectType>([3, 2, 1] as const); +expectType>(['c', 'b', 'a'] as const); +expectType>(['x', boolean, 1] as const); +expectType>([boolean, string, number] as const); +expectType>([4, 3, 2, 1] as const); +expectType>([4, 3, 2, 1, 0] as const); +expectType>([true, 1, 'x'] as const); + +// Optional/undefined +expectType>([3, 2, 1] as const); +expectType>({} as [3 | undefined, 2 | undefined, 1]); +expectType>([2, 1] as const); +expectType>([2, 1, undefined] as const); +expectType>([number, string] as const); +expectType>([string, number] as const); +expectType>([] as const); +expectType extends readonly ['a'] ? true : false>(true); +expectType>([3, 2, 1] as const); +expectType>([3, 2, 1] as const); +expectType>(['z', 'y', 'x'] as const); + +// Mixed types +expectType>([undefined, null, true, 'a', 1] as const); +expectType>(['end', 99, true, 'start'] as const); +expectType>([number as 1 | 2 | 3, 'x'] as const); +expectType>([1] as const); +expectType>([3, 2, 1] as const); +expectType>([string, 2, 1, boolean] as const); +expectType>(['b', string, number, 'a'] as const); +expectType>(['c', 'b', 'a'] as const); +expectType>({} as [1, 2, ...never[]]); +expectType>([3, 2, 1] as const); + +// Large tuples, readonly modifiers +expectType>({} as readonly [6, 5, 4, 3, 2, 1]); +expectType>({} as readonly ['d', 'c', 'b', 'a']); +expectType>({} as [6, 5, 4, 3, 2, 1]); +expectType>({} as ['d', 'c', 'b', 'a']); +expectType>([10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] as const); +expectType>(['h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'] as const); +expectType>([boolean, [number] as readonly number[]] as const); +expectType>(['z', 2, 1, [string] as string[]] as const); +expectType>(['e', 'd', 'c', 'b', 'a'] as const); +expectType>([boolean, boolean, boolean] as const); +expectType>(['y', 'x'] as const); +expectType>(['3', '2', '1'] as const); + +// Union +expectType>({} as [2, 1] | [4, 3]); +expectType>({} as ['a', 1] | [2, 'b']); + +// Spread +expectType>({} as [...boolean[], true]); +expectType>({} as [...number[], 2, 1]); +expectType>(['c', `on${string}`, 'a'] as const); +expectType>({} as [...boolean[], 'x', 1]); + +expectType>({} as [true, ...boolean[]]); +expectType>({} as [2, ...number[], 1]); +expectType>({} as ['end', ...number[], 1]); + +// In Use +declare function reverse(array: T): Reverse; +expectType<['d', 'c', 'b', 'a']>(reverse(['a', 'b', 'c', 'd']));