Интерфейсы в TypeScript — как использовать

В этом руководстве вы узнаете об интерфейсах TypeScript и о том, как их использовать для принудительной проверки типов.

Введение в интерфейсы TypeScript

Интерфейсы TypeScript определяют контракты в вашем коде. Они также предоставляют явные имена для проверки типов.

Начнем с простого примера:

function getFullName(person: {
    firstName: string;
    lastName: string
}) {
    return `${person.firstName} ${person.lastName}`;
}

let person = {
    firstName: 'John',
    lastName: 'Doe'
};

console.log(getFullName(person));

Выход:

John Doe

В этом примере компилятор TypeScript проверяет аргумент, который вы передаете в функцию getFullName().

Если аргумент имеет два свойства, типы которых являются строковыми, то компилятор TypeScript проходит проверку. В противном случае выдаст ошибку .

Как видно из кода, аннотация типа аргумента функции затрудняет чтение кода.

Чтобы решить эту проблему, TypeScript вводит концепцию интерфейсов.

В следующем примере используется интерфейс Person с двумя строковыми свойствами:

interface Person {
    firstName: string;
    lastName: string;
}

По соглашению имена интерфейсов пишутся в верхнем регистре. Они используют одну заглавную букву для разделения слов в именах. Например, Person, UserProfile и FullName.

После определения интерфейса Person вы можете использовать его как тип. И вы можете аннотировать параметр функции именем интерфейса:

function getFullName(person: Person) {
    return `${person.firstName} ${person.lastName}`;
}

let john = {
    firstName: 'John',
    lastName: 'Doe'
};

console.log(getFullName(john));

Код теперь легче читать, чем раньше.

Функция getFullName() будет принимать любой аргумент, который имеет два строковых свойства. И не обязательно иметь ровно два строковых свойства. См. следующий пример:

Следующий код объявляет объект с четырьмя свойствами:

let jane = {
   firstName: 'Jane',
   middleName: 'K.'
   lastName: 'Doe',
   age: 22
};

Поскольку объект jane имеет два строковых свойства firstName и lastName, вы можете передать его в функцию getFullName() следующим образом:

let fullName = getFullName(jane);
console.log(fullName); // Jane Doe

Дополнительные свойства

Интерфейс может иметь необязательные свойства. Чтобы объявить необязательное свойство, вы используете вопросительный знак( ?) в конце имени свойства в объявлении, например:

interface Person {
    firstName: string;
    middleName?: string;
    lastName: string;
}

В этом примере интерфейс Person имеет два обязательных  и одно необязательное свойство.

А ниже показано, как использовать интерфейс Person в функции getFullName() :

function getFullName(person: Person) {
    if(person.middleName) {
        return `${person.firstName} ${person.middleName} ${person.lastName}`;
    }
    return `${person.firstName} ${person.lastName}`;
}

Свойства только для чтения

Если свойства должны изменяться только при первом создании объекта, вы можете использовать ключевое слово readonly перед именем свойства:

interface Person {
    readonly ssn: string;
    firstName: string;
    lastName: string;    
}

let person: Person;
person = {
    ssn: '171-28-0926',
    firstName: 'John',
    lastName: 'Doe'
}

В этом примере свойство ssn изменить нельзя:

person.ssn = '171-28-0000';

Ошибка:

error TS2540: Cannot assign to 'ssn' because it is a read-only property.

Типы функций

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

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

interface StringFormat {
   (str: string, isUpper: boolean): string
}

Теперь вы можете использовать этот интерфейс функционального типа.

Ниже показано, как объявить переменную типа функции и присвоить ей значение функции того же типа:

let format: StringFormat;

format = function(str: string, isUpper: boolean) {
    return isUpper ? str.toLocaleUpperCase() : str.toLocaleLowerCase();
};

console.log(format('hi', true));

Выход:

HI

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

let format: StringFormat;

format = function(src: string, upper: boolean) {
    return upper ? src.toLocaleUpperCase() : src.toLocaleLowerCase();
};

console.log(format('hi', true));

Интерфейс StringFormat гарантирует, что все вызывающие функции, которые его реализуют, передают необходимые аргументы: string и boolean.

Следующий код также прекрасно работает, хотя lowerCase назначен функции, не имеющей второго аргумента:

let lowerCase: StringFormat;
lowerCase = function(str: string) {
    return str.toLowerCase();
}

console.log(lowerCase('Hi', false));

Обратите внимание, что второй аргумент передается при вызове функции lowerCase().

Типы классов

Если вы работали с Java или C#, вы можете обнаружить, что в основном интерфейс используется для определения контракта между несвязанными классами.

Например, следующий интерфейс Json может быть реализован любыми несвязанными классами:

interface Json {
   toJSON(): string
}

Далее объявляется класс, реализующий интерфейс Json :

class Person implements Json {
    constructor(private firstName: string,
        private lastName: string) {
    }
    toJson(): string {
        return JSON.stringify(this);
    }
}

В классе Person мы реализовали метод toJson() Json интерфейса SnNvbg==.

В следующем примере показано, как использовать класс Person :

let person = new Person('John', 'Doe');
console.log(person.toJson());

Выход:

{"firstName":"John","lastName":"Doe"}

Заключение

  • Интерфейсы TypeScript определяют контракты в коде и предоставляют явные имена для проверки типов.
  • Могут иметь необязательные свойства или свойства только для чтения.
  • Интерфейсы могут использоваться как типы функций.
  • Обычно используются как типы классов, которые заключают контракт между несвязанными классами в TypeScript.
Рейтинг
( Пока оценок нет )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript