Understand overload resolution, the this pseudo-parameter, and the variance rules that govern function assignability.
Open this lesson in KodokonAn overloaded function exposes a list of public signatures followed by a single implementation signature. An essential specification point: the latter is invisible from the outside; only the declared overloads are callable. Resolution walks the signatures from top to bottom and keeps the first compatible one: always place the most specific signature first, otherwise it will never be selected.
function parse(input: string): number;
function parse(input: string[]): number[];
function parse(input: string | string[]) {
return Array.isArray(input)
? input.map((s) => s.length)
: input.length;
}
const single = parse("abc"); // number
const many = parse(["a", "bb"]); // number[]TypeScript lets you declare a this pseudo-parameter in first position. It is entirely erased from the emitted JavaScript: it is a purely static constraint on the call context, checked when noImplicitThis is enabled. The ThisParameterType and OmitThisParameter utilities let you extract or remove it, which is essential for typing functions meant to be used with call, apply, or bind.
function greet(this: { name: string }, msg: string) {
return `${msg}, ${this.name}`;
}
type Ctx = ThisParameterType<typeof greet>;
// { name: string }
type Fn = OmitThisParameter<typeof greet>;
// (msg: string) => string
const user = { name: "Ada", greet };
user.greet("Hello"); // "Hello, Ada"
// greet("Hi"); // error: missing this contextVariance describes how assignability propagates through a type constructor. For functions, return types are covariant and, with strictFunctionTypes, parameters are contravariant: a function that accepts a wider type can replace one that accepts a narrower type, never the other way around. One deliberate exception: the parameters of methods declared in shorthand syntax remain bivariant, a historical compromise without which Array<Dog> would not be assignable to Array<Animal>. Since 4.7, you can annotate the expected variance with in and out, for example interface Producer<out T>, and the compiler verifies it.
interface Animal { name: string }
interface Dog extends Animal { bark(): void }
declare let f: (a: Dog) => void;
declare let g: (a: Animal) => void;
f = g; // OK: g accepts a wider type, that is safe
// g = f; // error with strictFunctionTypes
interface Sink { accept(a: Animal): void }
interface DogSink { accept(a: Dog): void }
declare let s: Sink;
declare let d: DogSink;
d = s; s = d; // both compile: bivariance!this parameter in the emitted JavaScript?