Разберись в разрешении перегрузок, псевдопараметре this и правилах вариантности, которые управляют присваиваемостью функций.
Открыть этот урок в KodokonПерегруженная функция выставляет список публичных сигнатур, за которыми идёт одна сигнатура реализации. Важнейший момент спецификации: последняя не видна снаружи; вызвать можно только объявленные перегрузки. Разрешение перебирает сигнатуры сверху вниз и берёт первую подходящую: всегда ставь самую специфичную сигнатуру первой, иначе она никогда не будет выбрана.
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 позволяет объявить псевдопараметр this на первой позиции. Он полностью стирается из порождаемого JavaScript: это чисто статическое ограничение на контекст вызова, проверяемое при включённом noImplicitThis. Утилиты ThisParameterType и OmitThisParameter позволяют извлечь его или убрать - это необходимо, чтобы типизировать функции, предназначенные для использования с call, apply или 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 contextВариантность описывает, как присваиваемость распространяется через конструктор типа. У функций возвращаемые типы ковариантны, а при strictFunctionTypes параметры контравариантны: функция, принимающая более широкий тип, может заменить ту, что принимает более узкий, но никогда наоборот. Одно намеренное исключение: параметры методов, объявленных в сокращённом синтаксисе, остаются бивариантными - исторический компромисс, без которого Array<Dog> нельзя было бы присвоить в Array<Animal>. Начиная с 4.7 ожидаемую вариантность можно аннотировать через in и out, например interface Producer<out T>, и компилятор её проверит.
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 функции в порождаемом JavaScript?