TypeScript тип any против всех

от автора

В жизни каждого разработчика на TypeScript наступает момент, когда ему хочется рвать все связи с типом any. А ведь по началу any казался таким милым! Сделай переменной аннотацию типа any и используй любое свойство и метод этой переменной так, как привык работать в JavaScript. Никаких тебе ошибок, все чинно и спокойно, по-старому.

Документация TypeScript оправдывает использование any только на время переноса кодовой базы из JavaScript в TypeScript, но считает постыдным его использование в полноценном проекте. Казалось бы, все хорошо, только в описании типов библиотечных функций самого TypeScript аннотации any встречаются. Очень полезный `JSON.parse`, один из таких примеров.

// из lib.es5.d.ts interface JSON {     parse(text: string, reviver?: (this: any, key: string, value: any) => any): any;     stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string;     stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string; }

Выйти из положения позволяет умение TypeScript объединять описания интерфейсов. Давайте разберемся как это можно исправить библиотечное описание интерфейса JSON.

Использование слияния интерфейсов и перегрузки методов
interface JSON {     parse(         text: string,         reviver?: (this: unknown, key: string, value: unknown) => unknown,     ): unknown; }

Песочница

Слияние деклараций интерфейсов

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

В простейшем случае описания просто добавляются. Следующий пример показывает счастливый TypeScript.

interface User {     name: string; }  interface User {     memberSince: number; }  const user: User = {     memberSince: 2022,     name: 'author' }

Песочница

В первом варианте описания интерфейс User содержит одно поле `name`, во втором варианте тоже одно поле, но с другим именем. TypeScript объединил оба описания в одно. Все ожидаемо.

Во втором варианте описания интерфейса можно повторить описание поля из первого. Но если так, то повторение должно быть полным. Совпадать должны и имена и типы полей.

interface User {     name: string;     memberSince: number; }  interface User {     memberSince: number; }  const user: User = {     memberSince: 2022,     name: 'author' }

Песочница

Если бы имена полей были одинаковыми, но разных вариантах поля имели разные типы, то TypeScript высказал бы свое недовольство кодом ошибки (2717). Как это видно в следующей песочнице.

interface User {     name: string;     memberSince: string;// пока еще все хорошо }  interface User {     memberSince: number;//ошибка }  const user: User = {     memberSince: 2022, // ошибка     name: 'author' }

Песочница

Как работает перегрузка функций в TypeScript

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

function getYear():number; function getYear(ticks: number): number; function getYear(iso:string):number; //скрытая от вызывающей стороны реализация function getYear(arg?: undefined| string|number):number{     if(typeof arg === 'undefined'){         return 2022;     }     return new Date(arg).getFullYear(); }

Песочница

Перегрузка методов интерфейса.

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

interface User{     login(token:string):void;     login(name: string, password: string):void;     login():void; }  declare const user:User;  //вариант 1 user.login("--secret--"); //вариант 2 user.login("admin","pa$5w||d"); //вариант 3 user.login();

Песочница

Мы обратим дополнительное внимание на то, что имена методов повторяются. Более того, при слиянии интерфейсов у TypeScript нет требования уникальности имени метода.

Этим мы и воспользуемся для решения вопроса с улучшенной типизацией метода parse интерфейса JSON

Слияние с библиотечным интерфейсом

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

interface JSON {     parse(         text: string,         reviver?: (this: unknown, key: string, value: unknown) => unknown,     ): unknown; } const x = JSON.parse("0", function ():unknown {     console.log(this);// TypeScript подсказывает, что this: unknown     return 42; });  console.log(x) // TypeScript подсказывает, что x:unknown

Теперь мы можем не опасаться что TypeScript перестанет нам помогать в борьбе за качественное ПО.


ссылка на оригинал статьи https://habr.com/ru/post/653779/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *