To implement this functionality, you must utilize the special ThisType<T> utility type. ThisType<T> is used in an object literal to contextualize the type of the methods' this
variable as T
. The unique thing about ThisType is that it cannot be defined by users; instead, it is a predefined empty interface {}
in the library files and is handled specially by the compiler for contextual typing.
For instance, you can define a function like so:
declare function myBarFunction<K extends string>(
props: readonly K[],
obj: ThisType<{ [P in K]: string }>
): void;
In this function, the generic type K
represents the key-like elements in the props
array. The ThisType of obj
is { [P in K]: string }
, which is essentially equivalent to Record<K, string>
using the Record<K, V>
utility type. Thus, any object literal passed as obj
will have a this
context with string-valued properties based on the keys in the props
array.
Let's test this out:
const props = ['bar'] as const; // <-- ensure const assertion
myBarFunction(props, {
get foo() {
return fooify(this.bar.toUpperCase()) // works fine
}
});
It's important to note that when declaring props
, you need a const
assertion to maintain the literal type 'bar'. Without it, const props = ["bar"]
would infer props
as string[]
, losing track of its specific type.
If you hover over this
, you'll see its type as {bar: string}
.
Check out the Playground link for the code example