Skip to content

Commit

Permalink
Merge pull request #7 from Resetand/types-improvements-2
Browse files Browse the repository at this point in the history
some typing improvements #2
  • Loading branch information
Resetand authored Mar 4, 2024
2 parents a93464f + 7e93688 commit 4e4bc55
Show file tree
Hide file tree
Showing 25 changed files with 137 additions and 63 deletions.
31 changes: 12 additions & 19 deletions src/_types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
export type IndexOf<A extends unknown[]> = {
[K in keyof A]: A[K] extends A[number] ? K : never;
}[number];

export type NullOrUndefined = null | undefined;
export type AnyFunction<TReturn = any> = (...args: any[]) => TReturn;
export type AnyPrimitive = string | number | bigint | boolean | symbol | null | undefined;
export type AnyRecord<K extends PropertyKey = PropertyKey, V = any> = Record<K, V>;
export type ClassConstructor<T = unknown> = new (...args: any[]) => T;
export type Class<T = unknown> = new (...args: any[]) => T;

/**
* Narrow type U to T, if T is a subset of U.
* If T is not a subset of U fallback to intersection between T and U
*
* @example
* type T1 = Narrow<number, 1 | 2>; // -> 1 | 2
* type T2 = Narrow<number, 1 | '2'>; // -> 1
* type T3 = Narrow<Function, Record<string, unknown>>; // -> Function & Record<string, unknown>
*/
export type Narrow<U, T> = Extract<T, U> extends never ? (T extends U ? T : T & U) : Extract<T, U>;

export type Guard<TGuarded = unknown, TArgs extends unknown[] = void[]> = TArgs extends void[]
? (value: unknown | TGuarded) => value is TGuarded
Expand Down Expand Up @@ -44,14 +48,3 @@ type ObjectSchema = { [K in PropertyKey]: TypeSchema };
export type TypeSchema = ObjectSchema | Guard | [] | [TypeSchema] | [TypeSchema, ...TypeSchema[]];

export type InferGuardType<TGuard> = TGuard extends Guard<infer TGuarded, any[]> ? TGuarded : never;

/**
* Narrow type U to T, if T is a subset of U.
* If T is not a subset of U fallback to union of T and U.
*
* @example
* type T1 = Narrow<number, 1 | 2>; // -> 1 | 2
* type T2 = Narrow<number, 1 | '2'>; // -> 1
* type T3 = Narrow<Function, Record<string, unknown>>; // -> Function & Record<string, unknown>
*/
export type Narrow<U, T> = Extract<T, U> extends never ? T & U : Extract<T, U>;
2 changes: 1 addition & 1 deletion src/guards/isArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
* isArray(new Array()); // -> true
* isArray({}); // -> false
*/
export default function isArray<T>(value: T | unknown[]): value is unknown[] {
export default function isArray(value: unknown): value is unknown[] {
return Array.isArray(value);
}
2 changes: 1 addition & 1 deletion src/guards/isBigInt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ import { isType } from '../_utils';
* isBigInt(''); // -> false
* isBigInt(1); // -> false
*/
export default function isBigInt<T>(value: T | bigint): value is bigint {
export default function isBigInt(value: unknown): value is bigint {
return isType(value, 'BigInt');
}
2 changes: 1 addition & 1 deletion src/guards/isBoolean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import { isType } from '../_utils';
* isBoolean(false); // -> true
* isBoolean(1); // -> false
*/
export default function isBoolean<T>(value: T | boolean): value is boolean {
export default function isBoolean(value: unknown): value is boolean {
return isType(value, 'Boolean');
}
4 changes: 2 additions & 2 deletions src/guards/isClass.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ClassConstructor } from '../_types';
import type { Class } from '../_types';
import { isType } from '../_utils';

/**
Expand All @@ -15,6 +15,6 @@ import { isType } from '../_utils';
* isClass(function() {}); // -> false
* isClass(new Function()); // -> false
*/
export default function isClass<T>(value: T | ClassConstructor): value is ClassConstructor {
export default function isClass(value: unknown): value is Class {
return isType(value, 'Function') && /^class\s/.test(Function.prototype.toString.call(value));
}
2 changes: 1 addition & 1 deletion src/guards/isDate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import { isType } from '../_utils';
* isDate(new Date('01.02.1971')); // -> true
* isDate(new Date('invalid date')); // -> false
*/
export default function isDate<T>(value: T | Date): value is Date {
export default function isDate(value: unknown): value is Date {
return !!value && isType(value, 'Date') && !isNaN(Number(value));
}
3 changes: 1 addition & 2 deletions src/guards/isEmpty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import isArray from './isArray';
import isString from './isString';
import isNil from './isNil';
import isInstanceOf from './isInstanceOf';
import { Narrow } from '../_types';

type EmptyValue = '' | null | undefined | [] | Record<PropertyKey, never> | Map<never, never> | Set<never>;

Expand All @@ -28,7 +27,7 @@ type EmptyValue = '' | null | undefined | [] | Record<PropertyKey, never> | Map<
* isEmpty('a'); // -> false
*
*/
export default function isEmpty<T>(value: T): value is Narrow<T, EmptyValue> {
export default function isEmpty(value: unknown): value is EmptyValue {
if (isPlainObject(value)) {
return !Object.keys(value).length;
}
Expand Down
2 changes: 1 addition & 1 deletion src/guards/isError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import { isType } from '../_utils';
* isError(new CustomError()); // -> true
* isError(''); // -> false
*/
export default function isError<T>(value: T | Error): value is Error {
export default function isError(value: unknown): value is Error {
return isType(value, 'Error') && value instanceof Error;
}
4 changes: 1 addition & 3 deletions src/guards/isFalsy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Narrow } from '../_types';

type Falsy = 0 | '' | false | null | undefined;

/**
Expand All @@ -15,6 +13,6 @@ type Falsy = 0 | '' | false | null | undefined;
* isFalsy(null); // -> true
* isFalsy(1); // -> false
*/
export default function isFalsy<T>(value: T): value is Narrow<T, Falsy> {
export default function isFalsy(value: unknown): value is Falsy {
return !value;
}
2 changes: 1 addition & 1 deletion src/guards/isFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ import { isType } from '../_utils';
*
* isFunction(class {}); // -> false (although class is a constructor function in JS, it's expected behavior)
*/
export default function isFunction<T>(value: T | AnyFunction): value is AnyFunction {
export default function isFunction(value: unknown): value is AnyFunction {
return isType(value, 'Function') && !isClass(value);
}
4 changes: 2 additions & 2 deletions src/guards/isHas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ type IsHasGuard = {
* isHas({ a: 1 })('a'); // -> true
* validate({value}, {value: is.isHas({ a: 1 })}); // -> true
*/
<T, P extends PropertyKey>(value: T, propertyName: P): value is T & { [K in P]: unknown };
<P extends PropertyKey>(propertyName: P): <T>(value: T) => value is T & { [K in P]: unknown };
<T, P extends PropertyKey>(value: unknown, propertyName: P): value is Record<P, unknown>;
<P extends PropertyKey>(propertyName: P): (value: unknown) => value is Record<P, unknown>;
};

const isHas: IsHasGuard = curriedGuard((value, propertyName) => {
Expand Down
4 changes: 2 additions & 2 deletions src/guards/isHasIn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ type IsHasInGuard = {
* isHasIn({ a: 1 })('a'); // -> true
* validate({value}, {value: is.isHasIn({ a: 1 })}); // -> true
*/
<T, P extends PropertyKey>(value: T, propertyName: P): value is T & { [K in P]: unknown };
<P extends PropertyKey>(propertyName: P): <T>(value: T) => value is T & { [K in P]: unknown };
<T, P extends PropertyKey>(value: unknown, propertyName: P): value is Record<P, unknown>;
<P extends PropertyKey>(propertyName: P): (value: unknown) => value is Record<P, unknown>;
};

const isHasIn: IsHasInGuard = curriedGuard((value, propertyName) => {
Expand Down
8 changes: 4 additions & 4 deletions src/guards/isInstanceOf.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ClassConstructor } from '../_types';
import type { Class } from '../_types';
import { curriedGuard } from '../_utils';

type InstanceOfGuard = {
Expand All @@ -15,11 +15,11 @@ type InstanceOfGuard = {
* isInstanceOf(Cls)(new Cls()); // -> true
* validate({value}, {value: is.InstanceOf(Cls)}); // -> true
*/
<T>(value: unknown, constructor: ClassConstructor<T>): value is T;
<T>(constructor: ClassConstructor<T>): (value: unknown) => value is T;
<T>(value: unknown, constructor: Class<T>): value is T;
<T>(constructor: Class<T>): (value: unknown) => value is T;
};

const isInstanceOf: InstanceOfGuard = curriedGuard((value: unknown, constructor: ClassConstructor) => {
const isInstanceOf: InstanceOfGuard = curriedGuard((value: unknown, constructor: Class) => {
return value instanceof constructor;
});

Expand Down
2 changes: 1 addition & 1 deletion src/guards/isIterable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
* isIterable({}); // -> false
* isIterable(null); // -> false
*/
export default function isIterable<T>(value: T | Iterable<unknown>): value is Iterable<unknown> {
export default function isIterable(value: unknown): value is Iterable<unknown> {
return Symbol.iterator in Object(value);
}
2 changes: 1 addition & 1 deletion src/guards/isNaN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
* isNaN(null); // -> false
* isNaN(undefined); // -> false
*/
export default function isNaN<T>(value: T | number): value is number {
export default function isNaN(value: unknown): value is number {
return Number.isNaN(value);
}
2 changes: 1 addition & 1 deletion src/guards/isNull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import { isType } from '../_utils';
* isNil(undefined); // -> false
* isNil(''); // -> false
*/
export default function isNull<T>(value: T | null): value is null {
export default function isNull(value: unknown): value is null {
return isType(value, 'Null');
}
3 changes: 1 addition & 2 deletions src/guards/isNumber.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { isType } from '../_utils';

/**
* Check if value a number literal or number created by `Number` constructor and not `NaN`
*
Expand All @@ -17,6 +16,6 @@ import { isType } from '../_utils';
* `NaN` is a special literal and in most cases you expect a number to be a valid number.
* If you want to check if value is number (including `NaN`) use `isNumber(value) || isNaN(value)` instead.
*/
export default function isNumber<T>(value: T | number): value is number {
export default function isNumber(value: unknown): value is number {
return isType(value, 'Number') && !Number.isNaN(value);
}
7 changes: 5 additions & 2 deletions src/guards/isPlainObject.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { AnyRecord } from '../_types';
import { isType } from '../_utils';

type PlainObject = {
[key: string]: unknown;
};

/**
* Check if value is a plain JavaScript object (excluding special classes or objects with other prototypes)
* It may be object literal `{}` or instance created by `Object` constructor
Expand All @@ -15,7 +18,7 @@ import { isType } from '../_utils';
* isPlainObject([]); // -> false
* isPlainObject(null); // -> false
*/
export default function isPlainObject<T>(value: T | AnyRecord): value is AnyRecord {
export default function isPlainObject(value: unknown): value is PlainObject {
if (!isType(value, 'Object')) {
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/guards/isPromise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import { isType } from '../_utils';
* isPromise({ then: () => void 0 }); // -> false (see `is.PromiseLike`)
* isPromise({}); // -> false
*/
export default function isPromise<T>(value: T | Promise<unknown>): value is Promise<unknown> {
export default function isPromise(value: unknown): value is Promise<unknown> {
return isType(value, 'Promise');
}
2 changes: 1 addition & 1 deletion src/guards/isPromiseLike.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ import isHasIn from './isHasIn';
* isPromiseLike({ then: () => void 0 }); // -> true
* isPromiseLike({}); // -> false
*/
export default function isPromiseLike<T>(value: T | PromiseLike<any>): value is PromiseLike<any> {
export default function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
return isPromise(value) || (isHasIn(value, 'then') && isFunction(value.then));
}
2 changes: 1 addition & 1 deletion src/guards/isRegExp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import { isType } from '../_utils';
* isRegExp(new RegExp('\\w+', 'g')); // -> true
* isRegExp(''); // -> false
*/
export default function isRegExp<T>(value: T | RegExp): value is RegExp {
export default function isRegExp(value: unknown): value is RegExp {
return isType(value, 'RegExp');
}
2 changes: 1 addition & 1 deletion src/guards/isString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import { isType } from '../_utils';
* isString(new String('')); // -> true
* isString(1); // -> false
*/
export default function isString<T>(value: T | string): value is string {
export default function isString(value: unknown): value is string {
return isType(value, 'String');
}
2 changes: 1 addition & 1 deletion src/guards/isSymbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import { isType } from '../_utils';
* isSymbol(Symbol.iterator); // -> true
* isSymbol(''); // -> false
*/
export default function isSymbol<T>(value: T | symbol): value is symbol {
export default function isSymbol(value: unknown): value is symbol {
return isType(value, 'Symbol');
}
2 changes: 1 addition & 1 deletion src/guards/isUndefined.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import { isType } from '../_utils';
* isNil(null); // -> false
* isNil(''); // -> false
*/
export default function isUndefined<T>(value: T | undefined): value is undefined {
export default function isUndefined(value: unknown): value is undefined {
return isType(value, 'Undefined');
}
Loading

0 comments on commit 4e4bc55

Please sign in to comment.