diff --git a/src/index.ts b/src/index.ts index 60e297d..7f1fa61 100644 --- a/src/index.ts +++ b/src/index.ts @@ -469,3 +469,47 @@ export function isType(payload: any, type: T): const name: string | undefined | null = (type as any).name return getType(payload) === name || Boolean(payload && payload.constructor === type) } + +type GlobalClassName = { + [K in keyof typeof globalThis]: (typeof globalThis)[K] extends AnyClass ? K : never +}[keyof typeof globalThis] + +/** + * Checks if a value is an instance of a class or a class name. Useful when you + * want to check if a value is an instance of a class that may not be defined in + * the current scope. For example, if you want to check if a value is an + * `OffscreenCanvas` instance, you might not want to do the song and dance of + * using `typeof OffscreenCanvas !== 'undefined'` and then shimming + * `OffscreenCanvas` if the types aren't around. + * + * @example + * if (isInstanceOf(value, 'OffscreenCanvas')) { + * // value is an OffscreenCanvas + * } + * + * @param value The value to recursively check + * @param class_ A string or class that the value should be an instance of + */ +export function isInstanceOf(value: unknown, class_: T): value is T +export function isInstanceOf( + value: unknown, + className: K +): value is (typeof globalThis)[K] +export function isInstanceOf(value: unknown, className: string): value is object +export function isInstanceOf(value: unknown, classOrClassName: AnyClass | string): boolean { + if (typeof classOrClassName === 'function') { + for (let p = value; p; p = Object.getPrototypeOf(p)) { + if (isType(p, classOrClassName)) { + return true + } + } + return false + } else { + for (let p = value; p; p = Object.getPrototypeOf(p)) { + if (getType(p) === classOrClassName) { + return true + } + } + return false + } +} diff --git a/test/index.test.ts b/test/index.test.ts index 02ac2a9..38b6d23 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -38,6 +38,7 @@ import { isEmptyObject, isOneOf, isFullObject, + isInstanceOf, } from '../src/index' // TODO: test isBlob @@ -378,3 +379,33 @@ test('type related tests', () => { // const a: Record = {} // a[myArray[1]] = myArray[0] }) + +test('isInstanceOf', () => { + expect(isInstanceOf(new Date(), Date)).toEqual(true) + expect(isInstanceOf(new Date(), 'Date')).toEqual(true) + expect(isInstanceOf(new Date(), Object)).toEqual(true) + expect(isInstanceOf(new Date(), 'Object')).toEqual(true) + + expect(isInstanceOf(new String(), String)).toEqual(true) + expect(isInstanceOf(new String(), 'String')).toEqual(true) + expect(isInstanceOf(new String(), Object)).toEqual(true) + expect(isInstanceOf(new String(), 'Object')).toEqual(true) + + expect(isInstanceOf(new Number(), Number)).toEqual(true) + expect(isInstanceOf(new Number(), 'Number')).toEqual(true) + expect(isInstanceOf(new Number(), Object)).toEqual(true) + expect(isInstanceOf(new Number(), 'Object')).toEqual(true) + + expect(isInstanceOf(new Boolean(), Boolean)).toEqual(true) + expect(isInstanceOf(new Boolean(), 'Boolean')).toEqual(true) + expect(isInstanceOf(new Boolean(), Object)).toEqual(true) + expect(isInstanceOf(new Boolean(), 'Object')).toEqual(true) + + expect(isInstanceOf([], Array)).toEqual(true) + expect(isInstanceOf([], 'Array')).toEqual(true) + expect(isInstanceOf([], Object)).toEqual(true) + expect(isInstanceOf([], 'Object')).toEqual(true) + + expect(isInstanceOf({}, Object)).toEqual(true) + expect(isInstanceOf({}, 'Object')).toEqual(true) +})