Skip to content

Instantly share code, notes, and snippets.

@crutchcorn
Last active September 9, 2020 22:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save crutchcorn/c130009f3e627a83ca133e4750db0f63 to your computer and use it in GitHub Desktop.
Save crutchcorn/c130009f3e627a83ca133e4750db0f63 to your computer and use it in GitHub Desktop.
Trying to overwrite the `this` type for functions within an object in TS
/**
* Trying to overwrite the `this` in a function even when the initial `this` typing is not the same
*
* The end goal is to get this to occur recursively to be able to provide typings for:
* https://github.com/evelynhathaway/bind-deep
*
* No, they don't need to be this strict - yes I like a challenge
*/
type GenericFn = (this: any, ...args: any[] | never) => any;
interface CallableFunctionCopyInterfaceOnlyForReference {
// Taken from https://github.com/microsoft/TypeScript/pull/27028/files#diff-a6b488d9bd802977827b535a3011c1f3
bind<T, A0, A extends any[], R>(this: (this: T, arg0: A0, ...args: A) => R, thisArg: T, arg0: A0): (...args: A) => R;
}
/* ---- ATTEMPT USING CallableFunction['bind'] ----- */
/* ------------------- FAIL ------------------------ */
class ToBindToCall {
static statProp = 3;
}
// `{} as any` is simply to force the type
// Binding to an interface prop like this seems to be disallowed from a syntax level?
const BoundWHello: CallableFunction['bind']<ToBindToCall, 3, []> = ({} as any);
const boundFun = BoundWHello(() => {});
/* ----------- ATTEMPT USING ThisType -------------- */
/* ------------------- FAIL ------------------------ */
/**
* This method particurlarly hurts because `noImplicitThis` is required for `ThisType` to work, which means
* that you couldn't leave random `this`s around
*/
class W {
static WItem = 3;
static Hello = function(this: undefined) {
return this;
}
}
class T {
static TItem = 34;
static OtherFunc = W.Hello;
}
// It seems that `ThisType` does not force `this` typing
const TBound: typeof T & ThisType<T> = T;
TBound.OtherFunc().TItem // This should be good
TBound.OtherFunc().WItem // This should not be
/* -------- ATTEMPT USING CUSTOM BindFunc ---------- */
/* ------------------- FAIL ------------------------ */
type BindFunc<NewThisType, FuncToBind extends GenericFn> =
(this: NewThisType, ...args: Parameters<FuncToBind>) =>
FuncToBind extends (this: infer TThis, ...args: any[] | never) => infer Q ? Q extends TThis ?
NewThisType : Q : undefined;
// `{} as any` is simply to force the type
const BoundWHello: BindFunc<T, typeof W.Hello> = {} as any;
BoundWHello()
/* ---------- ATTEMPT USING CUSTOM Bind ------------ */
/* ------------------- FAIL ------------------------ */
// This seems to not force `this` unfortunately
// FuncToBind & ThisType<NewThisType>;
type Bind<This, ToBind, ToBindKey = keyof ToBind> = {
[key in Extract<ToBindKey, string | number | symbol>]:
ToBindKey extends GenericFn ? BindFunc<This, ToBindKey> :
ToBindKey extends object ? Bind<This, ToBind> :
ToBindKey;
}
declare function bindDeep<This, ToBind>(thisObj: This, toBindArg: ToBind): Bind<This, ToBind>;
bindDeep(new T(), new W())
@crutchcorn
Copy link
Author

We may be able to get this working using some self-reference vudu:

microsoft/TypeScript#33050

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