Created
November 6, 2019 00:53
-
-
Save dubzzz/0914511305401a7694fbf471047e9098 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type EnableIf<T, U, ErrorMessage extends string = never> = T extends true ? U : ErrorMessage; | |
type Not<T> = T extends true ? false : true; | |
type And<T, U, V = true, W = true, X = true> = T extends true ? U extends true ? V extends true ? W extends true ? X extends true ? true : false : false : false : false : false; | |
type Or<T, U> = T extends true ? true : U extends true ? true : false; | |
type Extends<T, U> = T extends U ? true : false; | |
type IsPrimitive<T> = T extends object ? false : true; | |
type IsNever<T> = [T] extends [never] ? true : false | |
type IsUnknown<T> = And< | |
Not<IsNever<T>>, | |
Extends<T, unknown>, | |
Extends<unknown, T>, | |
Not<Extends<T, string>> // falsy for unknown bu truthy for any | |
>; | |
type IsAny<T> = And< | |
Not<IsNever<T>>, | |
Not<IsUnknown<T>>, | |
And<Extends<T, any>, Extends<any, T> extends true ? true : false> | |
>; | |
type Ctor<T> = { new(...args: any[]): T }; | |
// Type of the instances that might be produced by T | |
type FinalType<T> = T extends Ctor<infer U> ? U : T; | |
type Matcher<T> = { | |
not: NotMatcher<T>, | |
toBeInstanceOf<U>( | |
expected: EnableIf< | |
And< | |
Not<IsPrimitive<T>>, | |
Or<Extends<T, FinalType<U>>, Extends<FinalType<U>, T>> | |
>, | |
U | |
> | |
): T; | |
}; | |
type NotMatcher<T> = { | |
not: Matcher<T>, | |
toBeInstanceOf<U>( | |
expected: EnableIf< | |
Or< | |
IsPrimitive<T>, | |
And< | |
Not<Extends<T, FinalType<U>>>, | |
Not<IsAny<U>> | |
> | |
>, | |
U | |
> | |
): T; | |
}; | |
declare function expect<T = unknown>(t: T): Matcher<T>; | |
/****************************************/ | |
/* Declarations */ | |
/****************************************/ | |
class A { | |
a() { } | |
} | |
class ABis { | |
a() { } | |
} | |
class B { | |
b() { } | |
} | |
class C extends B { | |
c() { } | |
} | |
class HasStaticNameMethod { | |
constructor() {} | |
static plop() {} | |
} | |
function DefinesNameProp(): void {} | |
Object.defineProperty(DefinesNameProp, 'plop', { | |
configurable: true, | |
enumerable: false, | |
value: '', | |
writable: true, | |
}); | |
function LegacyClass(this: any): void { | |
this.a = 9; | |
} | |
function LegacyChildClass(this: any): void { | |
this.b = 8; | |
} | |
LegacyChildClass.prototype = Object.create(LegacyClass.prototype); | |
declare function foo<T>(): T; | |
/****************************************/ | |
/* Cases extracted from Jest Unit-Tests */ | |
/* & */ | |
/* Custom cases */ | |
/****************************************/ | |
// TRY ON Matcher | |
// Expected SUCCESS | |
expect(new Map()).toBeInstanceOf(Map); | |
expect([]).toBeInstanceOf(Array); | |
expect(new A()).toBeInstanceOf(A); | |
expect(new C()).toBeInstanceOf(B); | |
expect(new HasStaticNameMethod()).toBeInstanceOf(HasStaticNameMethod); | |
// -- | |
expect(new DefinesNameProp()).toBeInstanceOf(DefinesNameProp); | |
expect(new A()).toBeInstanceOf(A); | |
expect(new C()).toBeInstanceOf(B); | |
expect(new LegacyClass()).toBeInstanceOf(LegacyClass); | |
expect(foo<C>()).toBeInstanceOf(B); | |
expect(new String("a")).toBeInstanceOf(String); | |
expect(new LegacyChildClass()).toBeInstanceOf(LegacyClass); | |
expect(/\w+/).toBeInstanceOf(RegExp); | |
// Maybe SUCCESS | |
expect(foo<B>()).toBeInstanceOf(C); // foo might return a C | |
// Expected FAILURE | |
expect('a').toBeInstanceOf(String); | |
expect(1).toBeInstanceOf(Number); | |
expect(true).toBeInstanceOf(Boolean); | |
expect(new A()).toBeInstanceOf(B); | |
expect(Object.create(null)).toBeInstanceOf(A); | |
expect(undefined).toBeInstanceOf(String); | |
expect(null).toBeInstanceOf(String); | |
expect(/\w+/).toBeInstanceOf(function() {}); | |
expect(new DefinesNameProp()).toBeInstanceOf(RegExp); | |
// -- | |
expect({}).toBeInstanceOf(LegacyClass); | |
expect(new DefinesNameProp()).toBeInstanceOf(HasStaticNameMethod); | |
expect(foo<C>()).toBeInstanceOf(A); | |
expect(foo<A>()).toBeInstanceOf(C); | |
expect(new ABis()).toBeInstanceOf(A); | |
expect(new LegacyClass()).toBeInstanceOf(LegacyChildClass); | |
// TRY ON NotMatcher | |
// Expected SUCCESS | |
expect(new Map()).not.toBeInstanceOf(Map); | |
expect([]).not.toBeInstanceOf(Array); | |
expect(new A()).not.toBeInstanceOf(A); | |
expect(new C()).not.toBeInstanceOf(B); | |
expect(new HasStaticNameMethod()).not.toBeInstanceOf(HasStaticNameMethod); | |
// -- | |
expect(new DefinesNameProp()).not.toBeInstanceOf(DefinesNameProp); | |
expect(new A()).not.toBeInstanceOf(A); | |
expect(new C()).not.toBeInstanceOf(B); | |
expect(new LegacyClass()).not.toBeInstanceOf(LegacyClass); | |
expect(foo<C>()).not.toBeInstanceOf(B); | |
expect(new String("a")).not.toBeInstanceOf(String); | |
expect(new LegacyChildClass()).not.toBeInstanceOf(LegacyClass); | |
expect(/\w+/).not.toBeInstanceOf(RegExp); | |
// Maybe SUCCESS | |
expect(foo<B>()).not.toBeInstanceOf(C); // foo might return a C | |
// Expected FAILURE | |
expect('a').not.toBeInstanceOf(String); | |
expect(1).not.toBeInstanceOf(Number); | |
expect(true).not.toBeInstanceOf(Boolean); | |
expect(new A()).not.toBeInstanceOf(B); | |
expect(Object.create(null)).not.toBeInstanceOf(A); | |
expect(undefined).not.toBeInstanceOf(String); | |
expect(null).not.toBeInstanceOf(String); | |
expect(/\w+/).not.toBeInstanceOf<any>(function() {}); // FIXME | |
expect(new DefinesNameProp()).not.toBeInstanceOf(RegExp); | |
// -- | |
expect({}).not.toBeInstanceOf(LegacyClass); | |
expect(new DefinesNameProp()).not.toBeInstanceOf(HasStaticNameMethod); | |
expect(foo<C>()).not.toBeInstanceOf(A); | |
expect(foo<A>()).not.toBeInstanceOf(C); | |
expect(new ABis()).not.toBeInstanceOf(A); // FIXME | |
expect(new LegacyClass()).not.toBeInstanceOf(LegacyChildClass); | |
// DEBUG EXPERIMENTS | |
let instanceTest = /\w+/; | |
let classTest = function () { }; | |
type InstanceTest = typeof instanceTest; | |
type ClassTest = typeof classTest; | |
type ClassTestInstances = FinalType<ClassTest>; | |
type TestA = InstanceTest extends ClassTestInstances ? true : false; | |
type TestB = ClassTestInstances extends InstanceTest ? true : false; | |
type NotTestA = IsPrimitive<InstanceTest> | |
type NotTestB = Not<Extends<InstanceTest, ClassTestInstances>> | |
type TU = IsUnknown<any> | |
type TR = IsAny<never> | |
type TA = Extends<string, any> |
Author
dubzzz
commented
Nov 6, 2019
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment