Skip to content

Instantly share code, notes, and snippets.

@dubzzz
Created November 6, 2019 00:53
Show Gist options
  • Save dubzzz/0914511305401a7694fbf471047e9098 to your computer and use it in GitHub Desktop.
Save dubzzz/0914511305401a7694fbf471047e9098 to your computer and use it in GitHub Desktop.
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>
@dubzzz
Copy link
Author

dubzzz commented Nov 6, 2019

type IsTrue<T extends true> = true;
type IsFalse<T extends false> = false;

// Not
type Not<T> = T extends true ? false : true;

type Test_Not_A = IsFalse<Not<true>>;
type Test_Not_B = IsTrue<Not<false>>;

// And
type And2<T, U> = T extends true ? U extends true ? true : false : false;
type And<T, U, V = true, W = true, X = true> = And2<T, And2<U, And2<V, And2<W, X>>>>;

type Test_And_A = IsFalse<And<false, false>>;
type Test_And_B = IsFalse<And<false, true>>;
type Test_And_C = IsFalse<And<true, false>>;
type Test_And_D = IsTrue<And<true, true>>;
type Test_And_E = IsFalse<And<true, false, true>>;
type Test_And_F = IsTrue<And<true, true, true>>;

// Or
type Or2<T, U> = T extends true ? true : U extends true ? true : false;
type Or<T, U, V = false, W = false, X = false> = Or2<T, Or2<U, Or2<V, Or2<W, X>>>>;

type Test_Or_A = IsFalse<Or<false, false>>;
type Test_Or_B = IsTrue<Or<false, true>>;
type Test_Or_C = IsTrue<Or<true, false>>;
type Test_Or_D = IsTrue<Or<true, true>>;
type Test_Or_E = IsFalse<Or<false, false, false>>;
type Test_Or_F = IsTrue<Or<false, false, true>>;

// IsNever
// IsBoolean
// IsPrimitive
type IsPrimitive<T> = T extends object ? true : false;

type Test_IsPrimitive_A = IsPrimitive<never>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment