Skip to main content

The Self Parameter

Why is it there by default

All functions, by default, have a self parameter to preserve JavaScript behaviour.

Telling TypeScript not to allow this to be used will eliminate that parameter from its containing context.

Other workarounds are available for removing this too.

Disabling this self parameter should mainly be done when writing declarations for Lua interop purposes.

Removing it

this: void

This is a type-safe solution usable whenever describing something callable.

This tells TypeScript that this cannot be used in the context of this function.

this: void results in no self parameter to be generated.

Example

input.ts
declare function f(this: void, arg: string): void;
f("foo");
output.lua
f("foo")

Also useful if you have class methods which should be called with a dot . instead of a colon :.

Example

input.ts
declare class Class {
colon(arg: string): void;
dot(this: void, arg: string): void;
}

const c = new Class();
c.colon("foo");
c.dot("foo");
output.lua
local c = __TS__New(Class)
c:colon("foo")
c.dot("foo")

Common Lua libraries use callback functions that don't have a self parameter so make sure this is reflected in their declaration.

Example

input.ts
type Callback = (
this: void,
arg: string
) => void;

declare function useCallback(
this: void,
callback: Callback
): void;

useCallback(arg => {
console.log(arg);
});
output.lua
useCallback(function(arg)
print(arg)
end)

@noSelf

If you wish to specify that all functions in a class, interface or namespace should not have a context parameter, you can use the @noSelf annotation.

Example

input.ts
/** @noSelf **/
declare namespace Namespace {
function foo(arg: string): void;
}

Namespace.foo("foo");
output.lua
Namespace.foo("foo")

You can override @noSelf on a per-function basis by specifying a this parameter.

Example

input.ts
/** @noSelf **/
declare namespace Namespace {
function foo(this: any, arg: string): void;
}

Namespace.foo("foo");
output.lua
Namespace:foo("foo")

@noSelfInFile

If you want to specify that all functions in a file should have no context, you can use @noSelfInFile at the top of the file.

For more information on @noSelf and @noSelfInFile, please refer to Compiler Annotations.

noImplicitSelf

Use this option if you do not want implemented functions to have a self parameter.

Ambient functions (functions described, not implemented) as well as classes and interfaces ignore this option.

note

Use this with strict or noImplicitThis to ensure you don't use an "implicit this" type in your code.

When enabled, if this has a type other than an implicit any, a self parameter will be added for its containing function.

tsconfig.json
{
"tstl": {
"noImplicitSelf": true
}
}
input.ts
function f() {}
function f2(this: any) {}
const a = () => {};
class C {
method() {}
}
output.lua
function f() end
function f2(self) end
local a = function() end

local C = __TS__Class()
function C:method(self) end -- still has self

Assignment Errors

See the two types below.

type NoContext = (this: void) => void;
type UseContext = () => void;

TypeScript sees NoContext to be assignable to UseContext.

TypeScriptToLua does not.

Example

declare function useCallback(cb: (this: void, arg: string) => void);
// cb's type: (this: void, arg: string) => void

function callback(arg: string) {}
// callback's type: (arg: string) => void (implicit any)

useCallback(callback);

Error: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'.

This throws an error because callback's type is not assignable to cb's type since the latter has an implicit any type which changes how the function should be called.

To fix this, an arrow function can be used.

Example

input.ts
useCallback((arg) => callback(arg));
// argument type: (this: void, arg: string) => void
output.lua
useCallback(function(arg)
return callback(nil, arg)
end)

TypeScript says the arrow function has no context due to the parameter's signature making TypeScriptToLua accept the parameter.

Overloads

A similar error occurs if a function is overloaded and the call signature differs between how to use context:

declare function useCallback(f: () => {}): void;

declare function callback(this: void, s: string, n: number): void;
declare function callback(s: string);

useCallback(callback);

Error: Unsupported assignment of function with different overloaded types for 'this'. Overloads should all have the same type for 'this'.

It's best practice to avoid overloads with different context types.