Skip to content

Add IndexOf, LastIndexOf, IndicesOf, CountOf types #1155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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';
Expand All @@ -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';
17 changes: 13 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<T, U>` that ensures every member of `U` can successfully extract something from `T`.
- [`ExcludeStrict`](source/exclude-strict.d.ts) - A stricter version of `Exclude<T, U>` that ensures every member of `U` can successfully exclude something from `T`.

Expand Down
2 changes: 1 addition & 1 deletion source/array-splice.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type B = SplitArrayByIndex<[1, 2, 3, 4], 0>;
// type B = [[], [1, 2, 3, 4]];
```
*/
type SplitArrayByIndex<T extends UnknownArray, SplitIndex extends number> =
export type SplitArrayByIndex<T extends UnknownArray, SplitIndex extends number> =
SplitIndex extends 0
? [[], T]
: number extends T['length']
Expand Down
24 changes: 24 additions & 0 deletions source/count-of.d.ts
Original file line number Diff line number Diff line change
@@ -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<const T extends unknown[], const I>(array: T, item: I): CountOf<T, I>;
getCount(['a', 'b', 'a'], 'a');
//=> 2 instead of `number`
```

@author benzaria
@see Includes
@category Array
*/
export type CountOf<Array_ extends UnknownArray, Item, FromIndex extends number = 0> =
IndicesOf<Array_, Item, FromIndex> extends infer Indices extends number[]
? Indices['length']
: 0;
34 changes: 34 additions & 0 deletions source/extends-strict.d.ts
Original file line number Diff line number Diff line change
@@ -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<string | number, number>` => `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<number | string, string>
//=> false

type T = ExtendsStrict<string, number | string>
//=> true

type T = ExtendsStrict<string, string>
//=> true

type T = ExtendsStrict<never, never>
//=> true
```

@category Improved Built-in
*/
export type ExtendsStrict<Left, Right> =
IsNever<Left> extends true
? IsNever<Right>
: [Left] extends [Right]
? true
: false;
12 changes: 5 additions & 7 deletions source/includes.d.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -12,11 +13,8 @@ import type {Includes} from 'type-fest';
type hasRed<array extends any[]> = Includes<array, 'red'>;
```

@see CountOf
@category Array
*/
export type Includes<Value extends readonly any[], Item> =
Value extends readonly [Value[0], ...infer rest]
? IsEqual<Value[0], Item> extends true
? true
: Includes<rest, Item>
: false;
export type Includes<Array_ extends UnknownArray, Item, FromIndex extends number = 0> =
IndexOf<Array_, Item, FromIndex> extends -1 ? false : true;
79 changes: 79 additions & 0 deletions source/index-of.d.ts
Original file line number Diff line number Diff line change
@@ -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<T, 1>, without the extra logic.
*/
export type Increment<T extends number> = SumPositives<T, 1>;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you exporting it? It's not used elsewhere.

Copy link
Contributor Author

@benzaria benzaria Jun 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh i forgot about it i was using it in a arithmetic types I'm working on to Improve Sum, Substract with a range of 10999 number, with also Multiplication, Division. do u consider adding does types or it just to much ?


type _IndexOf<
Array_ extends UnknownArray, Item,
FromIndex extends number = 0,
Index extends number = 0,
> = (
Array_ extends readonly [infer Head, ...infer Tail]
? IsEqual<Head, Item> extends true
? SumPositives<Index, FromIndex>
: _IndexOf<Tail, Item, FromIndex, Increment<Index>>
: -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<readonly ['a', 'c', 'c'], 'c'>;
//=> 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<SplitArrayByIndex<Array_, FromIndex>[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<readonly ['a', 'c', 'c'], 'c'>;
//=> 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<Reverse<Array_>, Item, FromIndex> extends infer Index extends number
? Index extends -1
? -1
: SubtractPositives<Array_['length'], Increment<Index>>
: never
);
41 changes: 41 additions & 0 deletions source/indices-of.d.ts
Original file line number Diff line number Diff line change
@@ -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<Array_, Item, FromIndex> extends infer Index extends number
? Index extends -1
? Indices
: _IndicesOf<Array_, Item, Increment<Index>, [...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<readonly ['a', 'c', 'c'], 'c'>;
//=> [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<Array_, Item, FromIndex>;
10 changes: 5 additions & 5 deletions source/internal/array.d.ts
Original file line number Diff line number Diff line change
@@ -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';

/**
Expand Down Expand Up @@ -45,8 +45,8 @@ type B = StaticPartOfArray<A>;
*/
export type StaticPartOfArray<T extends UnknownArray, Result extends UnknownArray = []> =
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<V, [...Result, U]>
: Result
: T
Expand Down
29 changes: 23 additions & 6 deletions source/internal/type.d.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -40,9 +42,19 @@ export type HasMultipleCallSignatures<T extends (...arguments_: any[]) => 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 boolean> = [T] extends [false] ? false : true;
export type IsNotFalse<T extends boolean> = Not<IsFalse<T>>;

/**
Returns a boolean for whether the given `boolean` Union members are all `true`.
*/
export type IsTrue<T extends boolean> = ExtendsStrict<T, true>;

/**
Returns a boolean for whether the given `boolean` Union members are all `false`.
*/
export type IsFalse<T extends boolean> = ExtendsStrict<T, false>;

/**
Returns a boolean for whether the given type is primitive value or primitive type.
Expand All @@ -59,7 +71,7 @@ IsPrimitive<Object>
//=> false
```
*/
export type IsPrimitive<T> = [T] extends [Primitive] ? true : false;
export type IsPrimitive<T> = ExtendsStrict<T, Primitive>;

/**
Returns a boolean for whether A is false.
Expand Down Expand Up @@ -99,3 +111,8 @@ type C = IfNotAnyOrNever<never, 'VALID', 'IS_ANY', 'IS_NEVER'>;
*/
export type IfNotAnyOrNever<T, IfNotAnyOrNever, IfAny = any, IfNever = never> =
If<IsAny<T>, IfAny, If<IsNever<T>, IfNever, IfNotAnyOrNever>>;

/**
Determines if a type is either `never` or `any`.
*/
export type IsAnyOrNever<T> = Or<IsAny<T>, IsNever<T>>;
Loading