Skip to content

Instantly share code, notes, and snippets.

@etherealHero
Last active September 12, 2025 08:08
Show Gist options
  • Select an option

  • Save etherealHero/dea81b8adccf22eaff5aadb6eddbf0b8 to your computer and use it in GitHub Desktop.

Select an option

Save etherealHero/dea81b8adccf22eaff5aadb6eddbf0b8 to your computer and use it in GitHub Desktop.
//#region Polyfills & TypeScript utils
if (typeof Object.create !== "function") {
/**
* Polyfill
* {@link https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Object/create#polyfill}
*/
Object.create = (function () {
/** @constructor */
function Temp() {}
return function (proto) {
if (proto === null || (typeof proto !== "object" && typeof proto !== "function")) {
throw TypeError("Object prototype may only be an Object or null: " + proto);
}
Temp.prototype = proto;
var obj = new Temp();
Temp.prototype = null; // Avoid keeping a reference to the prototype
return obj;
};
})();
}
if (!Function.prototype.bind) {
/**
* Polyfill
* {@link https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#polyfill}
*/
Function.prototype.bind = function (
// @ts-ignore
oThis
) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
};
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
// @ts-ignore
fBound.prototype = new fNOP();
return fBound;
};
}
/**
* @template T
* @typedef {new (...args: any[]) => T} Constructor
*/
/**
* Extend Derived from Base.
* @template {Constructor<unknown>} Derived
* @template {Constructor<unknown>} Base
* @param {Derived} derivedConstructor - The derived class constructor.
* @param {Base} baseConstructor - The base class constructor.
* @returns {new (...args: ConstructorParameters<Derived>) => Omit<InstanceType<Base>, keyof InstanceType<Derived>> & InstanceType<Derived>}
*/
function extendConstructor(derivedConstructor, baseConstructor) {
derivedConstructor.prototype = Object.create(baseConstructor.prototype);
derivedConstructor.prototype.constructor = derivedConstructor;
// @ts-ignore
return /** @type {any} */ derivedConstructor;
}
/**
* @template T
* @typedef {Omit<T,
* | "constructor"
* | "toString"
* | "toLocaleString"
* | "valueOf"
* | "hasOwnProperty"
* | "isPrototypeOf"
* | "propertyIsEnumerable"
* >} NoObjectProps
*/
/**
* @template T
* @typedef {{ [K in keyof T]: T[K] }} Self
*/
/**
* @template T
* @typedef {Self<{
* [K in keyof NoObjectProps<T>]:
* T[K] extends (...args: any[]) => any
* ? T[K] // метод с сигнатурой
* : T[K] extends Function
* ? Function // все остальные методы
* : T[K] extends readonly (infer U)[]
* ? Pretty<U>[] // массивы рекурсивно
* : T[K] // остальные поля
* }>} Pretty
*/
/**
* Infer class interface
* @template {Constructor<unknown>} T
* @typedef {Pretty<InstanceType<T>>} IClass
*/
//#endregion
/** @typedef {IClass<Car>} ICar */
var Car = (function ($) {
var constructor = extendConstructor(Car, $);
/** @this {InstanceType<$> & Car} */
function Car() {
$.call(this, {});
this.speed = 1000;
}
return constructor;
})(Object);
/** @constructor @typedef {IClass<User>} IUser */
var User = (function ($) {
var constructor = extendConstructor(User, $);
/**
* @param {String} name
* @this {InstanceType<$> & User}
*/
function User(name) {
$.call(this, {});
this.name = name;
}
/** @type {Function} */
User.prototype.sayAll = /** @this {IUser} */ function () {
throw Error("Unimplemented method");
};
/** @param {Pick<IUser, "name">} to */
User.prototype.sayHello /** @this {IUser} */ = function (to) {
alert("Hello " + to.name + " from " + this.name);
};
return constructor;
})(Object);
/** @typedef {IClass<Admin>} IAdmin */
var Admin = (function ($) {
var constructor = extendConstructor(Admin, $);
/**
* @param {String} name
* @param {Number} accessLevel
* @this {InstanceType<$> & Admin}
*/
function Admin(name, accessLevel) {
$.call(this, name);
this.accessLevel = accessLevel;
}
/**
* @param {Number} count of repeat message
*/
Admin.prototype.sayAll = /** @this {IAdmin} */ function (count) {
var stack = [];
for (var i = 0; i < count; i++) {
stack.push("Admin " + this.name + " say all: Hello!");
}
alert(stack.join(", "));
};
return constructor;
})(User);
/** @typedef {IClass<SuperAdmin>} ISuperAdmin */
var SuperAdmin = (function ($) {
var constructor = extendConstructor(SuperAdmin, $);
/**
* @param {String} name
* @param {Number} accessLevel
* @this {InstanceType<$> & SuperAdmin}
*/
function SuperAdmin(name, accessLevel) {
$.call(this, name, accessLevel);
this.superAdminLevel = 100;
}
SuperAdmin.prototype.sayHello = /** @this {ISuperAdmin} */ function () {
alert("qq");
return "qq";
};
/**
* Отправить всем сообщение
* @param {String} message
* @param {String} postfix
*/
SuperAdmin.prototype.sayAll = /** @this {ISuperAdmin} */ function (message, postfix) {
var msg = "Message " + message + " from " + this.name + postfix;
alert(msg);
return msg;
};
return constructor;
})(Admin);
/** @typedef {IClass<WorkerUser>} IWorkerUser */
var WorkerUser = (function ($) {
var constructor = extendConstructor(WorkerUser, $);
/**
* @param {String} name
* @constructor @typedef {InstanceType<$> & WorkerUser} _ @this _
*/
function WorkerUser(name) {
$.call(this, name);
}
return constructor;
})(User);
/** @type {IUser} */
var alex = new User("Alex");
/** @type {IAdmin} */
var maxim = new Admin("Maxim", 1);
/** @type {IWorkerUser} */
var irina = new WorkerUser("Irina");
/** @type {ISuperAdmin} */
var andrew = new SuperAdmin("Andrew", 2);
/** @type {ICar} */
var toyota = new Car();
andrew.name;
andrew.sayHello();
andrew.sayHello();
maxim.sayAll(2);
andrew.sayAll("message", "LALALA");
maxim.sayHello(andrew);
getName(andrew);
getName(toyota);
// Expected diagnostic:
// Argument of type 'Self<{ speed: number; }>' is not assignable to parameter of type
// 'Pick<Self<{
// name: string;
// sayAll: Function;
// sayHello: (to: Pick<Self<...>, "name">) => void;
// }>, "name">'.
// TypeScript(2345)
nameOfSA(andrew);
/**
* @param {Pick<IUser, "name">} user
* @returns {String}
*/
function getName(user) {
return user.name;
}
/**
* @param {ISuperAdmin} SA
* @returns {String}
*/
function nameOfSA(SA) {
return SA.name;
}
/**
* @template {new (...args: ConstructorParameters<Constructor<InstanceType<typeof User>>>) => IUser} T
* @param {T} userConstructor
* @param {ConstructorParameters<T>} args
* @returns {IClass<T>} new user (infer returns type from input constructor)
*/
function createUser(userConstructor, args) {
var newUser = new (Function.prototype.bind.apply(userConstructor, [null].concat(args)))();
return newUser;
}
/**
* @template {new (...args: ConstructorParameters<Constructor<InstanceType<typeof User>>>) => IUser} T
* @param {T} userConstructor
* @param {ConstructorParameters<T>} args
* @returns {IUser} strict IUser cast returns type
*/
function createUserStrict(userConstructor, args) {
var newUser = new (Function.prototype.bind.apply(userConstructor, [null].concat(args)))();
return newUser;
}
// type is IUser
var newUser = createUser(User, ["Pavel"]);
// type is ISuperAdmin
var newSuperAdmin = createUser(SuperAdmin, ["John", 7]);
alert(newSuperAdmin.accessLevel);
// type is IUser
var newSuperAdminAsUser = createUserStrict(SuperAdmin, ["John", 8]);
// Recast allowed: type is IUser
/** @type {IUser} */
var newSuperAdmin2 = createUser(SuperAdmin, ["John", 9]);
var notAUser = createUser(Car, 1);
// Expected diagnostic:
// Argument of type 'new () => Omit<Object, "speed"> & Car'
// is not assignable to parameter of type
// 'new (...args: any[]) => Self<{
// name: string;
// sayAll: Function;
// sayHello: (to: Pick<Self<...>, "name">) => void;
// }>'.
// TypeScript(2345)
/** @param {boolean} cond */
function asserteq(cond) {
return cond ? "PASSED" : "FAIL";
}
/** @param {boolean} cond */
function assertne(cond) {
return !cond ? "PASSED" : "FAIL";
}
alert(
[
"// все должны быть от Object",
asserteq(toyota /**/ instanceof Object) + '\ttoyota \tinstanceof Object',
asserteq(alex /* */ instanceof Object) + '\talex \tinstanceof Object',
asserteq(maxim /* */ instanceof Object) + '\tmaxim \tinstanceof Object',
asserteq(irina /* */ instanceof Object) + '\tirina \tinstanceof Object',
asserteq(andrew /**/ instanceof Object) + '\tandrew \tinstanceof Object',
"",
"// всё от User",
assertne({} /* */ instanceof Car) + '\t{} \t!instanceof Car ',
assertne({} /* */ instanceof User) + '\t{} \t!instanceof User',
assertne(toyota /**/ instanceof User) + '\ttoyota \t!instanceof User',
asserteq(alex /* */ instanceof User) + '\talex \t instanceof User',
asserteq(maxim /* */ instanceof User) + '\tmaxim \t instanceof User',
asserteq(irina /* */ instanceof User) + '\tirina \t instanceof User',
asserteq(andrew /**/ instanceof User) + '\tandrew \t instanceof User',
"",
"// админы админы",
assertne({} /* */ instanceof Admin) + '\t{} \t!instanceof Admin',
assertne(toyota /**/ instanceof Admin) + '\ttoyota \t!instanceof Admin',
assertne(alex /* */ instanceof Admin) + '\talex \t!instanceof Admin',
assertne(irina /* */ instanceof Admin) + '\tirina \t!instanceof Admin',
asserteq(maxim /* */ instanceof Admin) + '\tmaxim \t instanceof Admin',
asserteq(andrew /**/ instanceof Admin) + '\tandrew \t instanceof Admin',
"",
"// СА только СА",
assertne({} /* */ instanceof SuperAdmin) + '\t{} \t!instanceof SuperAdmin',
assertne(alex /* */ instanceof SuperAdmin) + '\talex \t!instanceof SuperAdmin',
assertne(toyota /**/ instanceof SuperAdmin) + '\ttoyota \t!instanceof SuperAdmin',
assertne(maxim /* */ instanceof SuperAdmin) + '\tmaxim \t!instanceof SuperAdmin',
assertne(irina /* */ instanceof SuperAdmin) + '\tirina \t!instanceof SuperAdmin',
asserteq(andrew /**/ instanceof SuperAdmin) + '\tandrew \t instanceof SuperAdmin',
"",
"// верный констуктор",
asserteq(Var.ClassOf(toyota) /**/ === "Car") /* */ + '\tVar.ClassOf(toyota) \t=== "Car" ',
asserteq(Var.ClassOf(alex) /* */ === "User") /* */ + '\tVar.ClassOf(alex) \t\t=== "User" ',
asserteq(Var.ClassOf(irina) /* */ === "WorkerUser") /**/ + '\tVar.ClassOf(irina) \t\t=== "WorkerUser"',
asserteq(Var.ClassOf(maxim) /* */ === "Admin") /* */ + '\tVar.ClassOf(maxim) \t=== "Admin" ',
asserteq(Var.ClassOf(andrew) /**/ === "SuperAdmin") /**/ + '\tVar.ClassOf(andrew) \t=== "SuperAdmin"',
asserteq(Fnp.TypeOf(toyota) /* */ === "Car") /* */ + '\tFnp.TypeOf(toyota) \t=== "Car" ',
asserteq(Fnp.TypeOf(alex) /* */ === "User") /* */ + '\tFnp.TypeOf(alex) \t=== "User" ',
asserteq(Fnp.TypeOf(irina) /* */ === "WorkerUser") /**/ + '\tFnp.TypeOf(irina) \t=== "WorkerUser"',
asserteq(Fnp.TypeOf(maxim) /* */ === "Admin") /* */ + '\tFnp.TypeOf(maxim) \t=== "Admin" ',
asserteq(Fnp.TypeOf(andrew) /* */ === "SuperAdmin") /**/ + '\tFnp.TypeOf(andrew) \t=== "SuperAdmin"',
""
].join("\r\n")
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment