Typescript определить тип функции

Function, Functional Types¶

Функция — это ключевая концепция JavaScript. Функции присваиваются в качестве значений переменным и передаются как аргументы при вызове других функций. Поэтому не удивительно, что TypeScript очень много внимания уделяет возможностям функционального типа, к которым, начиная с текущей главы, повествование периодически будет возвращаться.

Function Types — тип функция¶

В TypeScript тип Function представляет собой одноименный JavaScript конструктор, являющийся базовым для всех функций. Тип Function можно указывать в аннотации типа тогда, когда о сигнатуре функции ничего неизвестно или в качестве значения могут выступать функции с несовместимыми сигнатурами.

function f1(p1: number): string  return p1.toString(); > function f2(p1: string): number  return p1.length; > let v1: Function = f1; let v2: Function = f2; 

При этом нельзя забывать, что по канонам статически типизированных языков, архитектуру программы нужно продумывать так, чтобы сводить присутствие высших в иерархии типов к нулю. В тех случаях, когда сигнатура функции известна, тип стоит конкретизировать при помощи определения более конкретных функциональных типов.

Поведение типа Function идентично одноимённому типу из JavaScript.

Functional Types — функциональный тип¶

Помимо того, что в TypeScript существует объектный тип Function , также существует функциональный тип, с помощью которого осуществляется описание сигнатур функциональных выражений.

Функциональный тип обозначается с помощью пары круглых скобок () , после которых располагается стрелка, а после неё обязательно указывается тип возвращаемого значения () => type . При наличии у функционального выражения параметров, их декларация заключается между круглых скобок (p1: type, p2: type) => type .

type FunctionalType = (p1: type, p2: type) => type; 

Если декларация сигнатуры функционального выражения известна, то рекомендуется использовать более конкретный функциональный тип, поскольку он в большей степени соответствует типизированной атмосфере.

type SumFunction = (a: number, b: number) => number; const sum: SumFunction = (a: number, b: number): number => a + b; 

Поведение функционального типа, указывающегося с помощью функционального литерала, идентично поведению типа Function , но при этом оно более конкретно и поэтому предпочтительнее.

this в сигнатуре функции¶

Ни для кого не будет секретом, что в JavaScript при вызове функций можно указать их контекст. В львиной доле случаев, возможность изменять контекст вызова функции является нежелательным поведением JavaScript, но только не в случае реализации конструкции, называемой функциональная примесь (functional mixins).

Функциональная примесь — это функция, в теле которой происходит обращение к членам, объявленным в объекте, к которому она “примешивается”. Проблем не возникнет, если подобный механизм реализуется в динамически типизированном языке, каким является JavaScript.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// .js class Animal  constructor()  this.type = 'animal'; > > function getType()  return this.type; > let animal = new Animal(); animal[getType.name] = getType; console.log(animal.getType()); // animal 

Но в статически типизированном языке такое поведение должно быть расценено как ошибочное, поскольку у функции нет присущего объектам признака this . Несмотря на это в JavaScript, а значит и в TypeScript, контекст самой программы (или, по другому, глобальный объект) является объектом. Это в свою очередь означает, что не существует места, в котором бы ключевое слово this привело к возникновению ошибки (для запрещения this в нежелательных местах нужно активировать опцию компилятора —noImplicitThis ). Но при этом за невозможностью предугадать поведение разработчика, в TypeScript ссылка this вне конкретного объекта ссылается на тип any , что лишает ide автодополнения. Для таких и не только случаев была реализована возможность декларировать тип this непосредственно в функциях.

this указывается в качестве первого параметра любой функции, и как обычный параметр имеет аннотацию типа, устанавливающую принадлежность к конкретному типу.

interface IT1  p1: string; > function f1(this: IT1): void <> 

Несмотря на то, что this декларируется в параметрах функции, таковыми оно не считается. Поведение функции с декларацией this аналогично поведению функции без декларации this . Единственное, на что стоит обратить внимание, что в случае указания принадлежности к типу, отличному от void , не получится вызвать функцию вне указанного контекста.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
interface IT1  p1: string; > function f1(this: void): void <> function f2(this: IT1): void <> function f3(): void <> f1(); // Ok f2(); // Error f3(); // Ok let v1 =  // v1: void;> f2: f2, >; v1.f2(); // Error let v2 =  // v2: void;> p1: '', f2: f2, >; v2.f2(); // Ok 

Кроме того, возможность ограничивать поведение ключевого слова this в теле функции призвано частично решить самую часто возникающую проблему, связанную с потерей контекста. Вряд ли найдется разработчик JavaScript, который может похвастаться, что ни разу не сталкивался с потерей контекста при передаче метода объекта в качестве функции обратного вызова (callback). В случаях, когда в теле метода происходит обращение через ссылку this к членам объекта, в котором он определен, то при потере контекста, в лучшем случае возникнет ошибка. В худшем, предполагающем, что в новом контексте будут присутствовать схожие признаки, возникнет трудно выявляемая ошибка.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
class Point  constructor(public x: number = 0, public y: number = 0) <> > class Animal  private readonly position: Point = new Point(); public move( clientX, clientY >: MouseEvent): void  this.position.x = clientX; this.position.y = clientY; > > let animal = new Animal(); // ошибка во время выполнения document.addEventListener('mousemove', animal.move); 

Для этих случаев TypeScript предлагает ограничить ссылку на контекст с помощью конкретизации типа ссылки this .

Так как реальный пример, иллюстрирующий полную картину, получается очень объемным, то ограничимся одним методом, реализующим обсуждаемое поведение.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
type IContextHandler = ( this: void, event: MouseEvent ) => void; class Controller  public addEventListener( type: string, handler: IContextHandler ): void <> > let animal = new Animal(); let controller = new Controller(); // ошибка во время выполнения controller.addEventListener('mousemove', animal.move); 

Стоит заметить, что одной конкретизации типа ссылки this в слушателе событий недостаточно. Для того чтобы пример заработал должным образом, необходимо конкретизировать ссылку this в самом слушателе событий.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
class Point  constructor(public x: number = 0, public y: number = 0) <> > class Animal  private readonly position: Point = new Point(); public move( this: Animal,  clientX, clientY >: MouseEvent ): void  // this.position.x = clientX; this.position.y = clientY; > > type IContextHandler = ( this: void, event: MouseEvent ) => void; class Controller  public addEventListener( type: string, handler: IContextHandler ): void <> > let animal = new Animal(); let controller = new Controller(); controller.addEventListener('mousemove', animal.move); // ошибка во время компиляции controller.addEventListener('mousemove', (event) => animal.move(event) ); // Ok 

Также стоит обратить внимание на одну неочевидную на первый взгляд деталь. Когда мы передаем слушатель, обернув его в стрелочную функцию, либо в метод функции .bind , ошибки не возникает только потому, что у передаваемой функции отсутствует декларация this .

Источник

Typescript определить тип функции

Каждая функция имеет тип, как и обычные переменные. Тип функции фактически представляет комбинацию типов параметров и типа возвращаемого значения. В общем виде определение типа функции выглядит следующим образом:

(параметр1: тип, параметр2: тип. параметрN: тип) => тип_результата;

В скобках идет перечисление через запятую параметров и их типов. После списка параметров через оператор => указывается тип возвращаемого функцией результата.

Например, возьмем следующую функцию:

Эта функция не имеет параметров и ничего не возвращает. Если функция ничего не возвращает, то фактически тип ее возвращаемого значения — void . Таким образом, функция hello имеет тип

Используя тип функции, мы можем определить переменные, константы и параметры этого типа. Например:

function hello ()< console.log("Hello TypeScript"); >; let message: ()=>void = hello; message();

В данном случае переменная message представляет любую функцию, которая не принимает параметров и ничего не возвращает.

Другой пример — функция, которая принимает параметры и возвращает некоторый результат:

function sum (x: number, y: number): number < return x + y; >;

Она имеет тип (x:number, y:number) => number; , то есть принимает два параметра number и возвращает значение типа number.

Также мы можем определять значения этого типа функции:

let op: (x:number, y:number) => number;

То есть переменная op представляет любую функцию, которая принимает два числа и которая возвращает число. Например:

function sum (x: number, y: number): number < return x + y; >; function subtract (a: number, b: number): number < return a - b; >; let op: (x:number, y:number) => number; op = sum; console.log(op(2, 4)); // 6 op = subtract; console.log(op(6, 4)); // 2

Здесь вначале переменная op указывает на функцию sum. И соответственно вызов op(2, 4) фактически будет представлять вызов sum(2, 4) . А затем op указывает на функцию subtract.

Функции как параметры других функций

Тип функции можно использовать как тип переменной, но он также может применяться для определения типа параметра другой функции:

function sum (x: number, y: number): number < return x + y; >; function multiply (a: number, b: number): number < return a * b; >; function mathOp(x: number, y: number, op: (a: number, b: number) => number): number < return op(x, y); >console.log(mathOp(10, 20, sum)); // 30 console.log(mathOp(10, 20, multiply)); // 200

Здесь в функции mathOp() третий параметр представляет функцию, которая принимает два параметра типа number и возвращает значение типа number. Соответственно при вызове функции mathOp() мы можем передать в нее, например, определенные здесь функции sum() или multiply() , которые соответствуют типу (a: number, b: number) => number

Если определенный тип функции предстоит очень часто использовать, то для него оптимальнее определить псевдоним и обращаться к типу по этому псевдониму:

type Operation = (a: number, b: number) => number; function mathOp(x: number, y: number, op: Operation): number < return op(x, y); >let sum: Operation = function(x: number, y: number): number < return x + y; >; console.log(mathOp(10, 20, sum)); // 30

В данном случае тип (a: number, b: number) => number проецируется на псевдоним Operation , который может использоваться для определения переменных и параметров.

Стрелочные функции

Для определения функций в TypeScript можно использовать стрелочные функции или arrow functions. Стрелочные функции представляют выражения типа (параметры) => тело функции . Например:

let sum = (x: number, y: number) => x + y; let result = sum(15, 35); // 50 console.log(result);

Тип параметров можно опускать:

let sum = (x, y) => x + y; let result = sum(15, 35); // 50 console.log(result);

Если стрелочная функция не требует параметров, то используются пустые круглые скобки. Если передается только один параметр, то скобки можно опустить:

let square = x => x * x; let hello = () => "hello world" console.log(square(5)); // 25 console.log(hello()); // hello world

Если тело функции представляет множество выражений, а не просто одно выражение, как в примере выше, тогда можно опять же заключить все выражения в фигурные скобки:

let sum = (x: number, y: number) => < x *= 2; return x + y; >; let result = sum(15, 35); // 65 console.log(result);

Стрелочные функции можно передавать в функцию вместо параметра, который представляет собой функцию:

function mathOp(x: number, y: number, operation: (a: number, b: number) => number): number < let result = operation(x, y); return result; >console.log(mathOp(10, 20, (x,y)=>x+y)); // 30 console.log(mathOp(10, 20, (x, y) => x * y)); // 200

Источник

Читайте также:  Генератор случайных чисел диапазоне java
Оцените статью