Список доступных хештегов:
Основные:
#theory — общая теория программирования, разбор теоретических вопросов с собеседования
#quiz — короткий вопрос на свободную тему в разработке с вариантами ответов
#useful — просто полезные вещи
#blog — посты в формате блога обо мне / на свободную тему
Подгруппы:
#javascript — всё, связанное с языком
#typescript — аналогично👆
#code — посты во встроенным в текст кодом, готовые примеры
#vite — посты, которые так или иначе затрагивают сборщик
#web — всё, касательно web разработки
#principles — принципы проектирования
#react — всё, касательно React
#patterns — всё о паттернах
#data — всё о данных и манипуляциях с ними
#news — новости
#python — всё, связанное с этим языком
#mobile — мобильная разработка
#design — штучки для дизайна
#github — интересности с гита
#chatbot — мои боты и всё, что с ними связано
Основные:
#theory — общая теория программирования, разбор теоретических вопросов с собеседования
#quiz — короткий вопрос на свободную тему в разработке с вариантами ответов
#useful — просто полезные вещи
#blog — посты в формате блога обо мне / на свободную тему
Подгруппы:
#javascript — всё, связанное с языком
#typescript — аналогично
#code — посты во встроенным в текст кодом, готовые примеры
#vite — посты, которые так или иначе затрагивают сборщик
#web — всё, касательно web разработки
#principles — принципы проектирования
#react — всё, касательно React
#patterns — всё о паттернах
#data — всё о данных и манипуляциях с ними
#news — новости
@deprecated
#python — всё, связанное с этим языком
#mobile — мобильная разработка
#design — штучки для дизайна
#github — интересности с гита
#chatbot — мои боты и всё, что с ними связано
Please open Telegram to view this post
VIEW IN TELEGRAM
TypeScript или JavaScript: что приоритетнее?
Есть много адептов и хейтеров TypeScript, в этом вопросе всё не так однозначно, просто выскажу своё мнение.
Я тащу TypeScript в любой проект, где у меня есть такая возможность, и категорически против использования чистого JavaScript без типов. Однако, есть случаи, где это просто необходимо, и использование TS только усложняет вашу жизнь.
Для меня TypeScript решает множество проблем, я его невероятно люблю и стараюсь погружаться в него всё глубже, а всё потому что разработка с ним для меня становится в разы более приятным занятием, чем без него. Код на TypeScript более читаемый, в него можно быстрее погрузиться и внести изменения, а приложения, написанные на нём, потенциально ловят гораздо меньше багов.
Также очень нравятся дополнительные типизируемые сущности в языке в лице типов и интерфейсов. Они позволяют понять что делает тот или иной код, порой, даже без чтения самого кода. Мы можем взглянуть на интерфейсы ввода/вывода данных и примерно понять что происходит в конкретной функции, что упростит её чтение и понимание в дальнейшем, если остается такая необходимость. То есть типы, помимо очевидной пользы в плане стабильности кода, несут и дополнительную функцию — документация кода.
Но у TypeScript есть и недостатки. Для меня главным недостатком является то, что компилятор TypeScript может быть неоптимален. Команда разработчиков старается быстро править все нюансы, но пару раз точно я ловил кейсы, где проблема производительности решалась изменением расширения файла на
Мой вывод заключается в том, что если TypeScript и ужасен, как об этом некоторые говорят, то чистый JavaScript для меня ещё страшнее. Я себя, по крайней мере на данный момент, больше отношу к адептам TS, нежели к его противникам.
Если говорить с точки зрения рынка, то современную фронтенд разработку без TypeScript мне представить не получается. За последние годы, навык разработчиков «Знание TypeScript» перешёл из категории «Желательно» в категорию «Обязательно» и очень плотно там закрепился. Прогнозов на обратный процесс пока точно не будет. Есть ощущение, что TypeScript с нами на очень долго.
Поэтому если вы хотите быть максимально конкурентноспособным на рынке, то стоит признать, что TypeScript — стандарт. Если вы начинающий разработчик, то придётся смириться с тем, что TypeScript придётся выучить.
Ну а вообще, учить нужно не языки, а программирование. Всех благ.
#web #typescript #javascript
Есть много адептов и хейтеров TypeScript, в этом вопросе всё не так однозначно, просто выскажу своё мнение.
Я тащу TypeScript в любой проект, где у меня есть такая возможность, и категорически против использования чистого JavaScript без типов. Однако, есть случаи, где это просто необходимо, и использование TS только усложняет вашу жизнь.
Для меня TypeScript решает множество проблем, я его невероятно люблю и стараюсь погружаться в него всё глубже, а всё потому что разработка с ним для меня становится в разы более приятным занятием, чем без него. Код на TypeScript более читаемый, в него можно быстрее погрузиться и внести изменения, а приложения, написанные на нём, потенциально ловят гораздо меньше багов.
Также очень нравятся дополнительные типизируемые сущности в языке в лице типов и интерфейсов. Они позволяют понять что делает тот или иной код, порой, даже без чтения самого кода. Мы можем взглянуть на интерфейсы ввода/вывода данных и примерно понять что происходит в конкретной функции, что упростит её чтение и понимание в дальнейшем, если остается такая необходимость. То есть типы, помимо очевидной пользы в плане стабильности кода, несут и дополнительную функцию — документация кода.
Но у TypeScript есть и недостатки. Для меня главным недостатком является то, что компилятор TypeScript может быть неоптимален. Команда разработчиков старается быстро править все нюансы, но пару раз точно я ловил кейсы, где проблема производительности решалась изменением расширения файла на
.js
, а значит, что никто не защищен от подобных казусов.Мой вывод заключается в том, что если TypeScript и ужасен, как об этом некоторые говорят, то чистый JavaScript для меня ещё страшнее. Я себя, по крайней мере на данный момент, больше отношу к адептам TS, нежели к его противникам.
Если говорить с точки зрения рынка, то современную фронтенд разработку без TypeScript мне представить не получается. За последние годы, навык разработчиков «Знание TypeScript» перешёл из категории «Желательно» в категорию «Обязательно» и очень плотно там закрепился. Прогнозов на обратный процесс пока точно не будет. Есть ощущение, что TypeScript с нами на очень долго.
Поэтому если вы хотите быть максимально конкурентноспособным на рынке, то стоит признать, что TypeScript — стандарт. Если вы начинающий разработчик, то придётся смириться с тем, что TypeScript придётся выучить.
Ну а вообще, учить нужно не языки, а программирование. Всех благ.
#web #typescript #javascript
Комплексные состояния
Есть такой код:
Как его можно исправить:
Почему я считаю что так лучше:
1. Проще следить за иммутабельностью состояния, так как не нужно постоянно разворачивать
2. Легче контролировать зависимости и сайд эффекты, если на состояние завязывается, например,
3. Визуально в использовании считается легче
5. Нет необходимости создавать ещё какой-то тип
В целом, можно и так оставить то. Работать будет, и даже уже сейчас работает. Но лично мне такое бьет по глазам.
@prog_way_blog —#typescript #web #review
Есть такой код:
const [userData, setUserData] = React.useState<IData>({
identifier: "",
password: "",
});
Как его можно исправить:
const [identifier, setIdentifier] = useState<string>("");
const [password, setPassword] = useState<string>("");
Почему я считаю что так лучше:
1. Проще следить за иммутабельностью состояния, так как не нужно постоянно разворачивать
prev
объект2. Легче контролировать зависимости и сайд эффекты, если на состояние завязывается, например,
useEffect
3. Визуально в использовании считается легче
4. useState
вместо React.useState
, ИМХО приятнее5. Нет необходимости создавать ещё какой-то тип
В целом, можно и так оставить то. Работать будет, и даже уже сейчас работает. Но лично мне такое бьет по глазам.
@prog_way_blog —#typescript #web #review
Лучшие практики типизации
В рамках ведения проектов и код-ревью я частенько натыкаюсь на странности написания TypeScript кода. В этом посте я постарался собрать самые частые ошибки, на которые точно стоит обратить внимание.
1. Лишний контекст
2
Сила
Проблема использования
3. Злоупотребление
4. Использование
5. Злоупотребление оператором
6. Отдельным и крайне важным для меня пунктом выделю оформление
Обратите внимание на то, что
Это самые частые ошибки, что мне приходилось комментировать. Все эти правила можно прочитать в официальном TypeScript Handbook и Google JavaScript Style Guide. Это не мои выдумки — это правила, закреплённые индустрией.
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #theory #typescript
В рамках ведения проектов и код-ревью я частенько натыкаюсь на странности написания TypeScript кода. В этом посте я постарался собрать самые частые ошибки, на которые точно стоит обратить внимание.
1. Лишний контекст
// плохо
type Person = {
personName: string;
personAge: string;
}
// замечательно
type Person = {
name: string;
age: string;
}
2
. enum
везде. Даже там, где не нужно. Сила
enum
заключается в том, что помимо перечисления внутри контекста, enum
можно использовать в качестве типа. Если вы не планируете использовать перечисления как тип, а лишь маппите что-либо в рамках одной сущности, то используйте константные объекты:const Status = {
Success: 'success',
Fail: 'fail'
} as const;
Проблема использования
enum
гораздо глубже, чем может показаться на первый взгляд. Думаю, что ей можно посвятить отдельный пост.3. Злоупотребление
any
. Я не противник использования any
в случаях, где это действительно необходимо, но многие, даже высокого грейда разработчики, не понимают зачем конкретно он нужен. Прежде, чем воткнуть any
, пожалуйста, попробуйте использовать unknown
. Это сделает ваш код гораздо безопаснее.4. Использование
Definite Assertion
оператора. Это когда вы утверждаете оператором восклицательного знака !
, что значение точно есть, хотя оно может быть и undefined
:// так делать не нужно, лучше добавить дополнительное условие
const foo = (arg?: string) => parseInt(arg!)
5. Злоупотребление оператором
as
. Ровно та же проблема, что с any
. Самый частый пример, что мне доводилось видеть:type Person = {
name: string
}
// плохо
const person = {
name: "Denis"
} as Person
// замечательно
const person: Person = {
name: "Denis"
}
6. Отдельным и крайне важным для меня пунктом выделю оформление
enum
. Согласно всем стайл-гайдам мира, enum
— сущность, группа, в единственном числе, оформленная определенным образом.// плохо
enum OPERATION_STATUS {}
enum STATUSES {}
enum STATUS {}
enum HTTPStatus {}
// замечательно
enum OperationStatus {}
enum Status {}
enum HttpStatus {}
Обратите внимание на то, что
Http
я написал не капсом. Аббревиатуры в названии любых переменных оформляются именно так, а не как HTTP
. Также название enum
-a пишется в PascalCase
. Таким же образом оформляются и ключи enum
-a:// плохо
enum Status {
SUCCESS,
FAIL
}
// замечательно
enum Status {
Success,
Fail
}
Это самые частые ошибки, что мне приходилось комментировать. Все эти правила можно прочитать в официальном TypeScript Handbook и Google JavaScript Style Guide. Это не мои выдумки — это правила, закреплённые индустрией.
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #theory #typescript
Как сохранять состояние в URL
В одном из прошлых постов я рассматривал требования к стажировке в Авито. Одним из требований к тестовому было кешировать значения в URL, в этом посте рассмотрим это более подробно и сделаем самую простую реализацию такого функционала.
Представим, что у нас есть строковый инпут для имени пользователя, его состояние в URL может выглядеть следующим образом:
Соответственно, нам нужен инструмент, который позволит управлять этим состоянием. Как бы хотелось это видеть? Лично мне — ровно так же, как работает и обычный
Первым аргументом будем указывать ключ в URL, а вторым — изначальное состояние. Далее рассмотрим внутреннюю имплементацию хука и начнём с его интерфейса, тут всё достаточно просто:
Реализуем наш собственный хук на базе
Ну а всё что нужно далее — реагировать на изменение состояния и обновлять URL в соответствии с ним. Если в инпуте вдруг окажется пустая строка, то мы просто удалим наше состояние из Query параметров
Да и всё. Всё работает.
Вот весь код, который, конечно же, не идеальный. Смысл поста — отразить основную суть.
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #code #typescript #web #react
В одном из прошлых постов я рассматривал требования к стажировке в Авито. Одним из требований к тестовому было кешировать значения в URL, в этом посте рассмотрим это более подробно и сделаем самую простую реализацию такого функционала.
Представим, что у нас есть строковый инпут для имени пользователя, его состояние в URL может выглядеть следующим образом:
www.site.com/?name=progway
Соответственно, нам нужен инструмент, который позволит управлять этим состоянием. Как бы хотелось это видеть? Лично мне — ровно так же, как работает и обычный
useState
. Тогда от этого и будем отталкиваться:const [name, setName] = useQueryState("name");
const [surname, setSurname] = useQueryState("surname", "Putnov");
Первым аргументом будем указывать ключ в URL, а вторым — изначальное состояние. Далее рассмотрим внутреннюю имплементацию хука и начнём с его интерфейса, тут всё достаточно просто:
type QueryValue = string | undefined;
function useQueryState<I extends QueryValue>(
queryKey: string,
defaultValue?: I,
) {
// тело хука
}
Реализуем наш собственный хук на базе
useSearchParams
из react-router-dom
для удобного доступа к состоянию URL-a. Если вы используете другой роутинг, можно использовать аналогичный механизм, который подойдёт именно в вашем проекте. Мы же будем его использовать для изменения URL-a и восстановления состояния из URL-a при маунте компонента. Если в URL-е уже есть состояние, мы будем использовать его. Если нет, будем использовать defaultValue
:const [searchParams, setSearchParams] = useSearchParams();
const [state, setState] = useState<QueryValue>(() => {
const targetValue = searchParams.get(queryKey);
if (targetValue !== null) {
return targetValue;
}
return defaultValue;
});
Ну а всё что нужно далее — реагировать на изменение состояния и обновлять URL в соответствии с ним. Если в инпуте вдруг окажется пустая строка, то мы просто удалим наше состояние из Query параметров
useLayoutEffect(() => {
setSearchParams((prev) => {
if (state) {
prev.set(queryKey, state);
} else {
prev.delete(queryKey);
}
return prev;
});
}, [state, queryKey, setSearchParams]);
Да и всё. Всё работает.
Вот весь код, который, конечно же, не идеальный. Смысл поста — отразить основную суть.
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #code #typescript #web #react
Дженерики
Сколько я не бился в попытках объяснить что такое дженерик, кажется, что эта тема многим даётся тяжело. В этом канале я планирую написать несколько постов на эту тему, в которых разберу самые частые варианты использования дженериков на практике начиная от легкого к сложному. И начнем с самого простого:
Generic Type — обобщённый тип — это тип, который позволяет создавать по своему подобию другие типы или же динамически вычислять типы в зависимости от входных данных. Тут я максимально отойду от какой-то академичности и приведу простую аналогию:
Тип — это переменная
Дженерик тип — это функция, которая возвращает тип
И дженерик типы называть функциями в корне неверно, зато такая аналогия для начала будет понятна большинству. Разберем на примере:
В этом примере у нас есть переменная, на основе которой мы можем получить некоторый объект
Уже в этом примере мы наблюдаем чем-то похожую ситуацию коду выше — мы создаём какую-то новую сущность на основе уже существующей. В первом случае — новый объект, а во втором — новый тип.
В таком контексте дженерики можно воспринимать как функции-конструкторы, которые, основываясь на некоторых входных параметрах, могут создавать новые типы. И из всего этого можно сделать вывод, что дженерики — это конструкторы типов.
На этом моменте у многих новичков часто возникает закономерный вопрос — а зачем их использовать? И ответ на него очень прост — почти за тем же, что и обычные функции.
Дженерик типы позволяют не дублировать код, а выносить некоторые общие части, чтобы далее их переиспользовать. Попробуем написать уже такой код, что легко встретится на практике:
В этом случае мы создали тип конструктор
Это самый базовый пример использования дженериков, который может встретиться на практике. Не забывайте, что дженерики есть не только у типов, а и у интерфейсов, классов, функций… Даже у React-компонентов и хуков (что в целом тоже либо класс либо функция).
Примеры с другими сущностями тоже хотелось бы показать, но уже в рамках других постов.
Спасибо за прочтение, это важно для меня ❤️
#web #theory #typescript
Сколько я не бился в попытках объяснить что такое дженерик, кажется, что эта тема многим даётся тяжело. В этом канале я планирую написать несколько постов на эту тему, в которых разберу самые частые варианты использования дженериков на практике начиная от легкого к сложному. И начнем с самого простого:
Generic Type — обобщённый тип — это тип, который позволяет создавать по своему подобию другие типы или же динамически вычислять типы в зависимости от входных данных. Тут я максимально отойду от какой-то академичности и приведу простую аналогию:
Тип — это переменная
Дженерик тип — это функция, которая возвращает тип
И дженерик типы называть функциями в корне неверно, зато такая аналогия для начала будет понятна большинству. Разберем на примере:
const name = "Denis"
const getPerson = (name) => ({
name,
type: "person"
})
const person = getPerson(name)
В этом примере у нас есть переменная, на основе которой мы можем получить некоторый объект
person
используя функцию getPerson
. А теперь рассмотрим нечто похожее в типах:type Status = "success" | "error"
type Response<T> = {
status: T,
message: string,
code: number,
}
type ApiResponse = Response<Status>
Уже в этом примере мы наблюдаем чем-то похожую ситуацию коду выше — мы создаём какую-то новую сущность на основе уже существующей. В первом случае — новый объект, а во втором — новый тип.
В таком контексте дженерики можно воспринимать как функции-конструкторы, которые, основываясь на некоторых входных параметрах, могут создавать новые типы. И из всего этого можно сделать вывод, что дженерики — это конструкторы типов.
На этом моменте у многих новичков часто возникает закономерный вопрос — а зачем их использовать? И ответ на него очень прост — почти за тем же, что и обычные функции.
Дженерик типы позволяют не дублировать код, а выносить некоторые общие части, чтобы далее их переиспользовать. Попробуем написать уже такой код, что легко встретится на практике:
// объявим сущности приложения
type User = {
name: string,
age: number,
}
type Task = {
completed: boolean,
title: string,
}
// ответ api в случае ошибки
type ResponseError = {
code: number;
message: string;
}
// ответ api в случае успеха
type ListResponseSuccess<T> = {
data: T[],
total: number;
}
// общий тип со всеми возможными
// состояниями ответа
type ListResponse<T> = ListResponseSuccess<T> | ResponseError
// типы для наших сущностей
type UserListResponse = ListResponse<User>
type TaskListReponse = ListResponse<Task>
// и их может быть сколько угодно...
type CarListResponse = ListResponse<Car>
type RoleListResponse = ListResponse<Role>
type UserGroupListResponse = ListResponse<UserGroup>
// и тд
В этом случае мы создали тип конструктор
ListResponse
, который в дальнейшем сможем использовать для типизации всего нашего API без лишнего дублирования общих частей.Это самый базовый пример использования дженериков, который может встретиться на практике. Не забывайте, что дженерики есть не только у типов, а и у интерфейсов, классов, функций… Даже у React-компонентов и хуков (что в целом тоже либо класс либо функция).
Примеры с другими сущностями тоже хотелось бы показать, но уже в рамках других постов.
Спасибо за прочтение, это важно для меня ❤️
#web #theory #typescript
Шпаргалка по Utility-типам в TypeScript
В этом посте разберу только самые часто используемые, посмотреть все utility-типы можно в официальной документации.
Делает все ключи необязательными
Обратное действие
Запрещает изменять поля объекта
Создаёт тип из ключей и значений, указанных отдельно
Выбирает только нужные ключи из типа и возвращает новый тип
Удаляет ненужные свойства из типа и возвращает новый тип
Возвращает тип параметров функции
Возвращает тип возвращаемого параметра из функции
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #typescript #useful
В этом посте разберу только самые часто используемые, посмотреть все utility-типы можно в официальной документации.
type Person = {
name: string;
surname: string;
age: number
}
Partial<T>
Делает все ключи необязательными
type PartialPerson = Partial<Person>
// то же самое, что
type PartialPerson = {
name?: string;
surname?: string;
age?: number
}
Required<T>
Обратное действие
Partial
— делает все ключи обязательнымиRequired<Partial<Person>> === Person
Readonly<Type>
Запрещает изменять поля объекта
const person: Readonly<Person> = {
name: "Denis",
surname: "Putnov",
age: 22
}
// ошибка, так как
// person нельзя изменять,
// только считывать (readonly)
person.name = 'Денис'
Record<Keys, Values>
Создаёт тип из ключей и значений, указанных отдельно
// ключи могут быть только строкой
// значения - только числом
type WordCounter = Record<string, number>
type Status = 'success' | 'error'
type StatusCounter = Record<Status, number>
// то же самое, что и Record ранее
type StatusCounter = {
success: number
error: number
}
Pick<Type, Keys>
Выбирает только нужные ключи из типа и возвращает новый тип
// из типа Person выбираем только свойства
// name и surname
type PersonName = Pick<Person, 'name' | 'surname'>
// то же самое
type PersonName = {
name: string
surname: string
}
Omit<Type, Keys>
Удаляет ненужные свойства из типа и возвращает новый тип
// Из типа Person исключаем свойство 'age'
type PersonName = Omit<Person, 'age'>;
// то же самое
type PersonName = {
name: string
surname: string
}
Parameters<Function>
Возвращает тип параметров функции
type Foo = (a: number, b: number) => string
type Params = Parameters<Foo>
Params // [a: number, b: number]
ReturnType<Function>
Возвращает тип возвращаемого параметра из функции
type Foo = (a: number, b: number) => string
type Return = ReturnType<Foo>
Return // string
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #typescript #useful
Как типизировать функцию мемоизации
В прошлом посте я разбирал реализацию функции мемоизации на JavaScript и упомянул, что типов там нет осознанно. В этом посте мы отдельно разберём типизацию такой функции, это будет полный разбор для самых маленьких.
Шпаргалка по используемым в посте utility-типам:
Итак, наша функция принимает в себя другую функцию. Сразу же типизируем это:
Далее было бы неплохо типизировать возвращаемую функцию. Для этого мы можем использовать следующую конструкцию:
И итоговый интерфейс функции у нас сильно растянется, но будет таким:
Ребята, стараюсь максимально упростить. Если плохо понимаете дженерики, постарайтесь более внимательно вчитываться в код.
Далее типизация кэша, тут всё просто:
По типизации, ключ — строка, а значение — тот тип, который возвращает оригинальная функция.
Далее рассмотрим итоговый код:
Надеюсь вышло понятно. Если что, любые вопросы можем обсудить в комментариях.
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #code #typescript
В прошлом посте я разбирал реализацию функции мемоизации на JavaScript и упомянул, что типов там нет осознанно. В этом посте мы отдельно разберём типизацию такой функции, это будет полный разбор для самых маленьких.
Шпаргалка по используемым в посте utility-типам:
Parameters<T> - возвращает тип аргументов функции T
ReturnType<T> - возвращает тип возвращаемых значений функции T
const foo = (a: number , b: number) => 5
type Args = Parameters<typeof foo>
// [a: number, b: number]
type Return = ReturnType<typeof foo>
// number
Итак, наша функция принимает в себя другую функцию. Сразу же типизируем это:
function memoize(func: (...args: any[]) => any) {
// ...
}
Далее было бы неплохо типизировать возвращаемую функцию. Для этого мы можем использовать следующую конструкцию:
(...args: Parameters<оригинальная функция>) => ReturnType<оригинальная функция>
. И вот мы сталкиваемся с проблемой, что для корректной типизации возвращаемой функции нам необходимы типы Parameters
и ReturnType
, которые обязательно принимают дженерик. В качестве дженерика выступает тип оригинальной функции. Для удобства вынесем тип оригинальной функции в дженерик функции memoize
:function memoize<T extends (...args: any[]) => any>(func: T) {
// ...
}
И итоговый интерфейс функции у нас сильно растянется, но будет таким:
type AnyFunction = (...args: any[]) => any;
type MemoizedFunction<T extends AnyFunction> =
(...args: Parameters<T>) => ReturnType<T>
function memoize<T extends AnyFunction>(func: T): MemoizedFunction {
// ...
}
Ребята, стараюсь максимально упростить. Если плохо понимаете дженерики, постарайтесь более внимательно вчитываться в код.
Далее типизация кэша, тут всё просто:
const cache = new Map<string, ReturnType<T>>();
По типизации, ключ — строка, а значение — тот тип, который возвращает оригинальная функция.
Далее рассмотрим итоговый код:
type AnyFunction = (...args: any[]) => any;
type MemoizedFunction<T extends AnyFunction> =
(...args: Parameters<T>) => ReturnType<T>
function memoize<T extends AnyFunction>(func: T): MemoizedFunction {
const cache = new Map<string, ReturnType<T>>();
return function(...args: Parameters<T>): ReturnType<T> {
const key = JSON.stringify(args);
if (!cache.has(key)) {
cache.set(key, func(...args));
}
return cache.get(key)!;
};
}
Надеюсь вышло понятно. Если что, любые вопросы можем обсудить в комментариях.
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #code #typescript
Что такое структурная типизация
Структурная типизация — это подход в языках программирования, который позволяет сократить количество возможных ошибок с типами, а также приведения типов, и сопоставлять не сами типы, а их структуры. Антонимом для структурной типизации является понятие номинальная типизация. Сразу же рассмотрим пример и сравним оба варианта:
У нас есть два типа
Номинальная типизация:
Но почему во втором случае ошибка? Потому что разные типы: функция ожидает на вход
А вот пример со структурной типизацией:
В этом случае всё отработает корректно, но только потому что
Также будет работать и в случае, если один из типов будет являться подмножеством другого:
Даже тут ошибки нет, а всё потому что тип
В этом и заключается весь смысл структурной типизации.
Спасибо за прочтение, это важно для меня ❤️
#web #theory #typescript
Структурная типизация — это подход в языках программирования, который позволяет сократить количество возможных ошибок с типами, а также приведения типов, и сопоставлять не сами типы, а их структуры. Антонимом для структурной типизации является понятие номинальная типизация. Сразу же рассмотрим пример и сравним оба варианта:
У нас есть два типа
Fireman
и Programmer
, а также функция, принимающая объект типа Fireman
:type Fireman = {
name: string
}
type Programmer = {
name: string
}
const fireman: Fireman = {
name: "Alex"
}
const programmer: Programmer = {
name: "Denis"
}
const foo = (person: Fireman) => { ... }
Номинальная типизация:
foo(fireman) // OK
foo(programmer) // Error
Но почему во втором случае ошибка? Потому что разные типы: функция ожидает на вход
Fireman
, а получает Programmer
. Сравнение происходит только по самому типу.А вот пример со структурной типизацией:
foo(fireman) // OK
foo(programmer) // OK
В этом случае всё отработает корректно, но только потому что
Programmer
и Fireman
абсолютно идентичны по своей структуре.Также будет работать и в случае, если один из типов будет являться подмножеством другого:
type Fireman = {
name: string;
}
type Programmer = {
name: string;
age: number
}
const fireman: Fireman = ...
const programmer: Programmer = ...
const foo = (person: Fireman) => { ... }
foo(fireman) // OK
foo(programmer) // OK
Даже тут ошибки нет, а всё потому что тип
Fireman
полностью включен в тип Programmer
и при вызове функции объект типа Programmer
полностью удовлетворяет типу ожидаемой структуры, а следовательно и ошибки не будет. Для языка не важно, что у Programmer
есть какие-то дополнительные поля. Главное, чтобы присутствовали все из типа Fireman
и имели такой же тип.В этом и заключается весь смысл структурной типизации.
Спасибо за прочтение, это важно для меня ❤️
#web #theory #typescript
В чем заключается разница между интерфейсом и типом?
Для меня большое удивление, что я до сих пор не смог найти нормального материала, где был бы описан этот вопрос. В этом после я планирую исправить эту несправедливость.
Тип — обозначается ключевым словом
Интерфейс — обозначается ключевым словом
Особенности типов:
1. Только типами можно создать типы-объединения (они же union type):
2. Только типами можно создавать кортежи, фиксируя тип элемента массива по индексу и длину массива:
3. Только с помощью типов можно создавать псевдонимы (они же alias):
4. Типами проще и красивее создавать описание функций:
Особенности интерфейсов:
1. Только интерфейсы имеют перегрузку типов:
2. Только интерфейсы могут быть имплементированы:
В основном, между интерфейсом и типом больше схожестей, чем отличий. Выше я перечислил основные различия между этими двумя сущностями, которого хватит для ответа на любом собеседовании.
Спасибо за прочтение, это важно для меня ❤️
#web #theory #typescript
Для меня большое удивление, что я до сих пор не смог найти нормального материала, где был бы описан этот вопрос. В этом после я планирую исправить эту несправедливость.
Тип — обозначается ключевым словом
type
— представляет собой либо описание структуры (объекта, функции…), либо набор других типов или структур:// набор типов
type Identifier = string | boolean;
// описание структуры
type Person = {
name: string;
age: number;
}
// набор типов или структур
type Foo = Person | string;
Интерфейс — обозначается ключевым словом
inderface
— может описывать только структуры:interface Props {
title: string;
visible?: boolean;
}
Особенности типов:
1. Только типами можно создать типы-объединения (они же union type):
type Identifier = string | number
type Animal = Dog | Cat
2. Только типами можно создавать кортежи, фиксируя тип элемента массива по индексу и длину массива:
type Cortage = [string, number, boolean]
3. Только с помощью типов можно создавать псевдонимы (они же alias):
type Name = string
type Author = Name
4. Типами проще и красивее создавать описание функций:
type Foo = (a: string) => number
interface Foo {
(a: string): number
}
Особенности интерфейсов:
1. Только интерфейсы имеют перегрузку типов:
interface Person {
name: string
}
interface Person {
age: number
}
const kate: Person = {
name: "Екатерина",
age: 24
}
2. Только интерфейсы могут быть имплементированы:
interface Person {}
class Fireman implements Person {}
В основном, между интерфейсом и типом больше схожестей, чем отличий. Выше я перечислил основные различия между этими двумя сущностями, которого хватит для ответа на любом собеседовании.
Спасибо за прочтение, это важно для меня ❤️
#web #theory #typescript
Сужение типов и уточняющие тайпгарды
Часто в TypeScript коде можно столкнуться с тем, что типы недостаточно точны. Это бывает в случаях, когда мы определяем типы, например, через
В таком случае мы получим
Тайпгарды — Type Guards — это языковые конструкции, проверки, которые позволяют определить или уточнить тип переменной средствами JavaScript. Тайпгарды бывают двух видов — уточняющие и определяющие.
Уточняющие тайпгарды — проверки, которые позволяют вывести более узкий тип из общего типа, например вывести
Определяющие же тайпгарды — проверки, которые позволяют вывести узкий тип из неизвестного типа, который в идеальном мире обозначается типом
Есть несколько способов сузить тип:
1. Через оператор
2. Через ключевое слово
3. Через ключевое слово
4. Через встроенные в язык функции проверки типа
5. Через
Также подобные проверки можно выносить в отдельные функции, которые обычно именуются по следующему шаблону —
В данном случае мы используем предикат для пользователя. Вообще, предикат — это базовый математический термин, но если кто-то не знал изи подзабыл, также напоминаю:
В нашем контексте, предикат — уникальное определяющее свойство сущности, относительно которого можно быть точно уверенным в её идентичности.
Если более просто, то мы знаем, что если
И всё это касается лишь уточняющих тайпгардов, пост об определяющих тайпгардах выйдет позже.
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #code #typescript
Часто в TypeScript коде можно столкнуться с тем, что типы недостаточно точны. Это бывает в случаях, когда мы определяем типы, например, через
union
— string | number
и других подобных случаях. Рассмотрим пример:const value: string | number = ...
parseInt(value) // ошибка
В таком случае мы получим
TypeError
, а всё потому что переменная value
имеет более общий тип, чем тот, что ожидает parseInt
— эта функция ожидает первым аргументом на вход только строку. В таком случае нам может помочь сужение типов, которое нам дают дополнительные проверки — тайпгарды.Тайпгарды — Type Guards — это языковые конструкции, проверки, которые позволяют определить или уточнить тип переменной средствами JavaScript. Тайпгарды бывают двух видов — уточняющие и определяющие.
Уточняющие тайпгарды — проверки, которые позволяют вывести более узкий тип из общего типа, например вывести
string
из типа string | number | boolean | null
.Определяющие же тайпгарды — проверки, которые позволяют вывести узкий тип из неизвестного типа, который в идеальном мире обозначается типом
unknown
Есть несколько способов сузить тип:
1. Через оператор
typeof
const value: string | number = ...
if (typeof value === 'string') {
// ошибки не будет, т.к.
// тип value в этом условии
// строго равняется "string"
parseInt(value)
}
2. Через ключевое слово
in
или метод hasOwnProperty
const value: any[] | number = ...
if ("map" in value) {
// value === array
value.map(...)
}
const animal = Cat | Dog = ...
if (animal.hasOwnProperty('meow')) {
// animal === Cat
animal.meow()
}
3. Через ключевое слово
instanceof
const person: Fireman | Programmer = ...
// при условии что Programmer - класс
// а person - инстанс класса
if (person instanceof Programmer) {
person.code()
}
4. Через встроенные в язык функции проверки типа
const value: any[] | number = ...
if (Array.isArray(value)) {
value.map(...)
}
5. Через
existed check
, например, для null
и undefined
const user: User | null = ...
if (user) {
console.log(user.name)
}
Также подобные проверки можно выносить в отдельные функции, которые обычно именуются по следующему шаблону —
is + <название типа или интерфейса>
, например, для типа User
будет логично создать тайпгард isUser
, вот небольшой пример:type Account = User | Admin | Manager
const isUser = (account: Account): account is User => {
return account.type === "user"
}
В данном случае мы используем предикат для пользователя. Вообще, предикат — это базовый математический термин, но если кто-то не знал изи подзабыл, также напоминаю:
В нашем контексте, предикат — уникальное определяющее свойство сущности, относительно которого можно быть точно уверенным в её идентичности.
Если более просто, то мы знаем, что если
account.type === “user”
, то перед нами ни что иное как User
. В этом случае тип аккаунта является предикатом, на основе которого можно делать выводы о типе аккаунта без дополнительных проверок.И всё это касается лишь уточняющих тайпгардов, пост об определяющих тайпгардах выйдет позже.
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #code #typescript