DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Christian Prado Ciokler
Christian Prado Ciokler

Posted on • Updated on

150+ Typescript one-liners [code snippets].

OneLiners

OneLiners are short snippets of code that are used in many places in the codebase. This is a collection of 150+ one-liners written in typescript.

Table of content

πŸ™ Hope you like πŸ‘ this post, let's get down to business. πŸš€

Array one liners ☝️


1. Cast a value as an array

export const castArray = <T, _>(value: T | T[]): T[] => (Array.isArray(value) ? value : [value]);
// castArray(1); // [1]
// castArray([1, 2, 3]); // [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

2. Check if an array is empty

export const isEmpty = <T, _>(arr: T[]): boolean => Array.isArray(arr) && !arr.length;
// isEmpty([]); // true
// isEmpty([1, 2, 3]); // false
Enter fullscreen mode Exit fullscreen mode

3. Clone an array

export const clone = <T, _>(arr: T[]): T[] => [...arr];
// clone([1,2,3]); // [1,2,3]
Enter fullscreen mode Exit fullscreen mode

4. Compare two arrays

export const isEqual = <T, _>(a: T[], b: T[]): boolean => JSON.stringify(a) === JSON.stringify(b);
// isEqual([1, 2, 3], [1, 2, 3]); // true
// isEqual([1, 2, 3], [1, '2', 3]); // false
Enter fullscreen mode Exit fullscreen mode

5. Compare two arrays regardless of order

export const isEqualWithoutOrder = <T, _>(a: T[], b: T[]): boolean =>
JSON.stringify([...new Set(a)].sort()) === JSON.stringify([...new Set(b)].sort());
// isEqualWithoutOrder([1, 2, 3], [1, 2, 3]); // true
// isEqualWithoutOrder([1, 2, 3], [1, 3, 2]); // true
// isEqualWithoutOrder([1, 2, 3], [1, '2', 3]); // false
Enter fullscreen mode Exit fullscreen mode

6. Convert an array of objects to a single object

export const toObject = <T extends Record<string, any>, K extends keyof T>(arr: T[], key: K): Record<string, T> =>
arr.reduce((a, b) => ({ ...a, [b[key]]: b }), {});
// toObject(
// [
// { id: '1', name: 'Alpha', gender: 'Male' },
// { id: '2', name: 'Bravo', gender: 'Male' },
// { id: '3', name: 'Charlie', gender: 'Female' },
// ],
// 'id'
// );
{
'1': { id: '1', name: 'Alpha', gender: 'Male' },
'2': { id: '2', name: 'Bravo', gender: 'Male' },
'3': { id: '3', name: 'Charlie', gender: 'Female' },
}
Enter fullscreen mode Exit fullscreen mode

7. Convert an array of strings to numbers

export const toNumbers = (arr: string[]): number[] => arr.map(Number);
// toNumbers(['2', '3', '4']); // [2, 3, 4]
Enter fullscreen mode Exit fullscreen mode

8. Count by the properties of an array of objects

export const countBy = <T extends Record<string, string>, K extends keyof T>(
  arr: T[],
  prop: K
): Record<string, number> =>
  arr.reduce((prev, curr) => ((prev[curr[prop]] = ++prev[curr[prop]] || 1), prev), {} as Record<string, number>);
// countBy(
// [
// { branch: 'audi', model: 'q8', year: '2019' },
// { branch: 'audi', model: 'rs7', year: '2020' },
// { branch: 'ford', model: 'mustang', year: '2019' },
// { branch: 'ford', model: 'explorer', year: '2020' },
// { branch: 'bmw', model: 'x7', year: '2020' }
// ],
// 'branch'
// );
// { 'audi': 2, 'ford': 2, 'bmw': 1 }
Enter fullscreen mode Exit fullscreen mode

9. Count the occurrences of a value in an array

export const countOccurrences = <T, _>(arr: T[], val: T): number => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);
// countOccurrences([2, 1, 3, 3, 2, 3], 2); // 2
Enter fullscreen mode Exit fullscreen mode

10. Count the occurrences of array elements

export const countOccurrencesElements = <T extends string | number>(arr: T[]): Record<T, number> =>
  arr.reduce((prev, curr) => ((prev[curr] = ++prev[curr] || 1), prev), {} as Record<T, number>);
// countOccurrencesElements([2, 1, 3, 3, 2, 3]); // { '1': 1, '2': 2, '3': 3 }
Enter fullscreen mode Exit fullscreen mode

11. Create an array of cumulative sum

export const accumulate = (arr: number[]): number[] =>
  arr.reduce((a, b, i) => (i === 0 ? [b] : [...a, b + a[i - 1]]), [0]);
// accumulate([1, 2, 3, 4]); // [1, 3, 6, 10]
Enter fullscreen mode Exit fullscreen mode

12. Create an array of numbers in the given range

export const range = (min: number, max: number): number[] => [...Array(max - min + 1).keys()].map((i) => i + min);
// range(5, 10); // [5, 6, 7, 8, 9, 10]
Enter fullscreen mode Exit fullscreen mode

13. Find the closest number from an array

export const closest = (arr: number[], n: number): number => arr.sort((a, b) => Math.abs(a - n) - Math.abs(b - n))[0];
// closest([29, 87, 8, 78, 97, 20, 75, 33, 24, 17], 50); // 33
Enter fullscreen mode Exit fullscreen mode

14. Find the index of the last matching item of an array

export const lastIndex = <T, _>(arr: T[], predicate: (a: T) => boolean): number =>
arr.map((item) => predicate(item)).lastIndexOf(true);
// lastIndex([1, 3, 5, 7, 9, 2, 4, 6, 8], (i) => i % 2 === 1); // 4
// lastIndex([1, 3, 5, 7, 9, 8, 6, 4, 2], (i) => i > 6); // 5
Enter fullscreen mode Exit fullscreen mode

15. Find the index of the maximum item of an array

export const indexOfMax = (arr: number[]): number => arr.reduce((prev, curr, i, a) => (curr > a[prev] ? i : prev), 0);
// indexOfMax([1, 3, 9, 7, 5]); // 2
// indexOfMax([1, 3, 7, 7, 5]); //
Enter fullscreen mode Exit fullscreen mode

16. Find the index of the minimum item of an array

export const indexOfMin = (arr: number[]): number => arr.reduce((prev, curr, i, a) => (curr < a[prev] ? i : prev), 0);
// indexOfMin([6, 4, 8, 2, 10]); // 3
// indexOfMin([6, 4, 2, 2, 10]); // 2
Enter fullscreen mode Exit fullscreen mode

17. Find the length of the longest string in an array

export const findLongest = (words: string[]): number => Math.max(...words.map((el) => el.length));
// findLongest(['always', 'look', 'on', 'the', 'bright', 'side', 'of', 'life']); // 6
Enter fullscreen mode Exit fullscreen mode

18. Find the maximum item of an array by given key

export const maxBy = <T extends Record<string, any>, K extends keyof T>(arr: T[], key: K): T =>
  arr.reduce((a, b) => (a[key] >= b[key] ? a : b), {} as T);
// const people = [
// { name: 'Bar', age: 24 },
// { name: 'Baz', age: 32 },
// { name: 'Foo', age: 42 },
// { name: 'Fuzz', age: 36 }
// ];
// maxBy(people, 'age'); // { name: 'Foo', age: 42 }
Enter fullscreen mode Exit fullscreen mode

19. Find the maximum item of an array

export const max = (arr: number[]): number => Math.max(...arr);
// max([1, 3, 9, 7, 5]); // 9
Enter fullscreen mode Exit fullscreen mode

20. Find the minimum item of an array by given key

export const minBy = <T extends Record<string, any>, K extends keyof T>(arr: T[], key: K): T =>
  arr.reduce((a, b) => (a[key] < b[key] ? a : b), {} as T);
// const people = [
// { name: 'Bar', age: 24 },
// { name: 'Baz', age: 32 },
// { name: 'Foo', age: 42 },
// { name: 'Fuzz', age: 36 },
// ];
// minBy(people, 'age'); // { name: 'Bar', age: 24 }
Enter fullscreen mode Exit fullscreen mode

21. Find the minimum item of an array

export const min = (arr: number[]): number => Math.min(...arr);
// min([1, 3, 9, 7, 5]); // 1
Enter fullscreen mode Exit fullscreen mode

22. Get all arrays of consecutive elements

export const getConsecutiveArrays = <T, _>(arr: T[], size: number): T[][] =>
  size > arr.length ? [] : arr.slice(size - 1).map((_, i) => arr.slice(i, size + i));
// getConsecutiveArrays([1, 2, 3, 4, 5], 2); // [[1, 2], [2, 3], [3, 4], [4, 5]]
// getConsecutiveArrays([1, 2, 3, 4, 5], 3); // [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
// getConsecutiveArrays([1, 2, 3, 4, 5], 6); // []
Enter fullscreen mode Exit fullscreen mode

23. Get all n-th items of an array

export const getNthItems = <T, _>(arr: T[], nth: number): T[] => arr.filter((_, i) => i % nth === nth - 1);
// getNthItems([1, 2, 3, 4, 5, 6, 7, 8, 9], 2); // [2, 4, 6, 8]
// getNthItems([1, 2, 3, 4, 5, 6, 7, 8, 9], 3); // [3, 6, 9]
Enter fullscreen mode Exit fullscreen mode

24. Get indices of a value in an array

export const indices = <T>(arr: T[], value: T): number[] =>
  arr.reduce((acc, v, i) => (v === value ? [...acc, i] : acc), [] as number[]);
// indices(['h', 'e', 'l', 'l', 'o'], 'l'); // [2, 3]
// indices(['h', 'e', 'l', 'l', 'o'], 'w'); // []
Enter fullscreen mode Exit fullscreen mode

25. Get the average of an array

export const average = (arr: number[]): number => arr.reduce((a, b) => a + b, 0) / arr.length;
// average([1, 2, 3, 4, 5]); // 3
Enter fullscreen mode Exit fullscreen mode

26. Get the intersection of arrays

export const getIntersection = <T, _>(a: T[], ...arr: T[][]): T[] =>
[...new Set(a)].filter((v) => arr.every((b) => b.includes(v)));
// getIntersection([1, 2, 3], [2, 3, 4, 5]); // [2, 3]
// getIntersection([1, 2, 3], [2, 3, 4, 5], [1, 3, 5]); // [3]
Enter fullscreen mode Exit fullscreen mode

27. Get the rank of an array of numbers

export const ranking = (arr: number[]): number[] => arr.map((x, y, z) => z.filter((w) => w > x).length + 1);
// ranking([80, 65, 90, 50]); // [2, 3, 1, 4]
// ranking([80, 80, 70, 50]); // [1, 1, 3, 4]
// ranking([80, 80, 80, 50]); // [1, 1, 1, 4]
Enter fullscreen mode Exit fullscreen mode

28. Get the sum of an array of numbers

export const sum = (arr: number[]): number => arr.reduce((a, b) => a + b, 0);
// sun([1, 2, 3, 4, 5]); // 15
Enter fullscreen mode Exit fullscreen mode

29. Get the unique values of an array

export const unique = <T>(arr: T[]): T[] => [...new Set(arr)];
// unique([1, 2, 3, 1, 4, 4, 5]); // [1, 2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

30. Get union of arrays

export const union = <T, _>(...arr: T[][]): T[] => [...new Set(arr.flat())];
// union([1, 2], [2, 3], [3]); // [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

31. Group an array of objects by a key

export const groupBy = <T extends Record<string, any>, K extends keyof T>(arr: T[], key: K): Record<string, T[]> =>
  arr.reduce((acc, item) => ((acc[item[key]] = [...(acc[item[key]] || []), item]), acc), {} as Record<string, T[]>);
// groupBy(
// [
// { branch: 'audi', model: 'q8', year: '2019' },
// { branch: 'audi', model: 'rs7', year: '2020' },
// { branch: 'ford', model: 'mustang', year: '2019' },
// { branch: 'ford', model: 'explorer', year: '2020' },
// { branch: 'bmw', model: 'x7', year: '2020' }
// ],
// 'branch'
// );
{
// audi: [
// { branch: 'audi', model: 'q8', year: '2019' },
// { branch: 'audi', model: 'rs7', year: '2020' }
// ],
// bmw: [
// { branch: 'bmw', model: 'x7', year: '2020' }
// ],
// ford: [
// { branch: 'ford', model: 'mustang', year: '2019' },
// { branch: 'ford', model: 'explorer', year: '2020' }
// ],
// }
Enter fullscreen mode Exit fullscreen mode

32. Intersperse element between elements

export const intersperse = <T>(a: T[], s: T): T[] => [...Array(2 * a.length - 1)].map((_, i) => (i % 2 ? s : a[i / 2]));
// intersperse(['A', 'B', 'C'], '/'); // ['A', '/', 'B', '/', 'C']
// intersperse([<li>A</li>, <li>B</li>, <li>C</li>], <li>/</li>); // [<li>A</li>, <li>/</li>, <li>B</li>, <li>/</li>, <li>C</li>]
Enter fullscreen mode Exit fullscreen mode

33. Merge two arrays

export const merge = <T, _>(a: T[], b: T[]): T[] => [...a, ...b];
// merge([1, 2, 3], [4, 5, 6]); // [1, 2, 3, 4, 5, 6]
Enter fullscreen mode Exit fullscreen mode

34. Partition an array based on a condition

// export const partition = <T, _>(arr: T[], criteria: (a: T) => boolean): T[][] =>
// arr.reduce((acc, i) => (acc[criteria(i) ? 0 : 1].push(i), acc), [[], []]);
// partition([1, 2, 3, 4, 5], (n) => n % 2); // [[1, 3, 5], [2, 4]]
Enter fullscreen mode Exit fullscreen mode

35. Remove duplicate values in an array

export const removeDuplicate = <T, _>(arr: T[]): T[] => arr.filter((i) => arr.indexOf(i) === arr.lastIndexOf(i));
// removeDuplicate(['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']); // ['h', 'e', 'w', 'r', 'd']
Enter fullscreen mode Exit fullscreen mode

36. Remove falsy values from array

export const removeFalsy = <T, _>(arr: T[]): T[] => arr.filter(Boolean);
// ['a string', true, 5, 'another string']
Enter fullscreen mode Exit fullscreen mode

37. Repeat an array

export const repeat = <T, _>(arr: T[], n: number): T[] => Array(n).fill(arr).flat();
repeat([1, 2, 3], 3); // [1, 2, 3, 1, 2, 3, 1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

38. Shuffle an array

export const shuffle = <T, _>(arr: T[]): T[] =>
arr
.map((a) => ({ sort: Math.random(), value: a }))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value);
// shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); // [9, 1, 10, 6, 8, 5, 2, 3, 7, 4]
Enter fullscreen mode Exit fullscreen mode

39. Sort an array of items by given key

export const sortBy = <T extends Record<string, any>, K extends keyof T>(arr: T[], k: K): T[] =>
  arr.concat().sort((a, b) => (a[k] > b[k] ? 1 : a[k] < b[k] ? -1 : 0));
// const people = [
// { name: 'Foo', age: 42 },
// { name: 'Bar', age: 24 },
// { name: 'Fuzz', age: 36 },
// { name: 'Baz', age: 32 }
// ];
// sortBy(people, 'age');

// [
// { name: 'Bar', age: 24 },
// { name: 'Baz', age: 32 },
// { name: 'Fuzz', age: 36 },
// { name: 'Foo', age: 42 },
// ]
Enter fullscreen mode Exit fullscreen mode

40. Sort an array of numbers

export const sort = (arr: number[]): number[] => arr.sort((a, b) => a - b);
// sort([1, 5, 2, 4, 3]); // [1, 2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

41. Split an array into chunks

export const chunk = <T>(arr: T[], size: number): T[][] =>
  arr.reduce((acc, e, i) => (i % size ? acc[acc.length - 1].push(e) : acc.push([e]), acc), [] as T[][]);
// chunk([1, 2, 3, 4, 5, 6, 7, 8], 3); // [[1, 2, 3], [4, 5, 6], [7, 8]]
// chunk([1, 2, 3, 4, 5, 6, 7, 8], 4); // [[1, 2, 3, 4], [5, 6, 7, 8]]
Enter fullscreen mode Exit fullscreen mode

42. Swap the rows and columns of a matrix

export const transpose = <T>(matrix: T[][]): T[][] => matrix[0].map((col, i) => matrix.map((row) => row[i]));
// transpose([
// // [
// [1, 2, 3], // [1, 4, 7],
// [4, 5, 6], // [2, 5, 8],
// [7, 8, 9] // [3, 6, 9],
// ]); // ]
Enter fullscreen mode Exit fullscreen mode

43. Swap two array items

export const swapItems = <T, _>(a: T[], i: number, j: number): T[] =>
(a[i] && a[j] && [...a.slice(0, i), a[j], ...a.slice(i + 1, j), a[i], ...a.slice(j + 1)]) || a;
// swapItems([1, 2, 3, 4, 5], 1, 4); // [1, 5, 3, 4, 2]
Enter fullscreen mode Exit fullscreen mode

44. Get all subsets of an array

export const getSubsets = <T>(arr: T[]): T[][] =>
  arr.reduce((prev, curr) => prev.concat(prev.map((k) => k.concat(curr))), [[]] as T[][]);
// getSubsets([1, 2]); // [[], [1], [2], [1, 2]]
// getSubsets([1, 2, 3]); // [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
Enter fullscreen mode Exit fullscreen mode

Date one liners ☝️


1. Add AM PM suffix to an hour

export const suffixAmPm = (h: number): string => `${h % 12 === 0 ? 12 : h % 12}${h < 12 ? 'am' : 'pm'}`;
// suffixAmPm(0); // '12am'
// suffixAmPm(5); // '5am'
// suffixAmPm(12); // '12pm'
// suffixAmPm(15); // '3pm'
// suffixAmPm(23); // '11pm'
Enter fullscreen mode Exit fullscreen mode

2. Calculate the number of difference days between two dates

export const diffDays = (date: Date, otherDate: Date): number =>
Math.ceil(Math.abs(date.valueOf() - otherDate.valueOf()) / (1000 * 60 * 60 * 24));
// diffDays(new Date('2014-12-19'), new Date('2020-01-01')); // 1839
Enter fullscreen mode Exit fullscreen mode

3. Calculate the number of months between two dates

export const monthDiff = (startDate: Date, endDate: Date): number =>
Math.max(0, (endDate.getFullYear() - startDate.getFullYear()) * 12 - startDate.getMonth() + endDate.getMonth());
// monthDiff(new Date('2020-01-01'), new Date('2021-01-01')); // 12
Enter fullscreen mode Exit fullscreen mode

4. Compare two dates

export const compare = (a: Date, b: Date): boolean => a.getTime() > b.getTime();
// compare(new Date('2020-03-30'), new Date('2020-01-01')); // true
Enter fullscreen mode Exit fullscreen mode

5. Convert a date to YYYY-MM-DD format

export const formatYmd = (date: Date): string => date.toISOString().slice(0, 10);
// formatYmd(new Date()); // 2020-05-06
Enter fullscreen mode Exit fullscreen mode

6. Convert a date to YYYY-MM-DD HH:MM:SS format

export const formatYmdHis = (date: Date): string => date.toISOString().slice(0, 19);
// formatYmdHis(new Date()); // 2020-05-06T15:00:00.000Z
Enter fullscreen mode Exit fullscreen mode

7. Convert seconds to hh:mm:ss format

export const formatSeconds = (s: number): string => new Date(s * 1000).toISOString().substr(11, 8);
// formatSeconds(200); // 00:03:20
// formatSeconds(500); // 00:08:20
Enter fullscreen mode Exit fullscreen mode

8. Extract year, month, day, hour, minute, second and millisecond from a date

export const extract = (date: Date): string[] =>
  date
    .toISOString()
    .split(/[^0-9]/)
    .slice(0, -1);
// extract(new Date()); // ['2020', '05', '06', '15', '00', '00', '00']
Enter fullscreen mode Exit fullscreen mode

9. Format a date for the given locale

export const format = (date: Date, locale: string): string => new Intl.DateTimeFormat(locale).format(date);
// format(new Date(), 'pt-BR'); // 06/05/2020
Enter fullscreen mode Exit fullscreen mode

10. Get the current quarter of a date

export const getQuarter = (d = new Date()): number => Math.ceil((d.getMonth() + 1) / 3);
// getQuarter(new Date('2020-01-01')); // 1
Enter fullscreen mode Exit fullscreen mode

11. Get the current timestamp in seconds

export const tseconds = (): number => Math.floor(new Date().getTime() / 1000);
// ts(); // 1588888888
Enter fullscreen mode Exit fullscreen mode

12. Get the day of the year from a date

export const dayOfYear = (date: Date): number =>
Math.floor((date.valueOf() - new Date(date.getFullYear(), 0, 0).valueOf()) / (1000 * 60 * 60 * 24));
// dayOfYear(new Date(2020, 04, 16)); // 137
Enter fullscreen mode Exit fullscreen mode

13. Get the first date in the month of a date

export const getFirstDate = (d = new Date()): Date => new Date(d.getFullYear(), d.getMonth(), 1);
// getFirstDate(new Date('2020-01-01')); // 2020-01-01
Enter fullscreen mode Exit fullscreen mode

14. Get the last date in the month of a date

export const getLastDate = (d = new Date()): Date => new Date(d.getFullYear(), d.getMonth() + 1, 0);
// getLastDate(new Date('2020-02-01')); // 2020-02-29
Enter fullscreen mode Exit fullscreen mode

15. Get the month name of a date

export const getMonthName = (date: Date): string =>
  [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December'
  ][date.getmonth()];
// getMonthName(new Date('2020-01-01')); // January
Enter fullscreen mode Exit fullscreen mode

16.Get the number of days in given month

export const daysInMonth = (month: number, year: number): number => new Date(year, month, 0).getDate();
// daysInMonth(2, 2020); // 29
Enter fullscreen mode Exit fullscreen mode

17. Get the timezone string

export const getTimezone = (): string => Intl.DateTimeFormat().resolvedOptions().timeZone;
// getTimezone(); // 'Asia/Saigon'
Enter fullscreen mode Exit fullscreen mode

18. Get the tomorrow date

export const tomorrow: Date = ((d) => new Date(d.setDate(d.getDate() + 1)))(new Date());
// tomorrow; // 2020-05-07
Enter fullscreen mode Exit fullscreen mode

19. Get the total number of days in a year

export const numberOfDays = (year: number): number => (new Date(year, 1, 29).getDate() === 29 ? 366 : 365);
// numberOfDays(2020); // 366
Enter fullscreen mode Exit fullscreen mode

20. Get the weekday of a date

export const getWeekday = (date: Date): string =>
  ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][date.getday()];
// getWeekday(new Date('2020-01-01')); // Sunday
Enter fullscreen mode Exit fullscreen mode

21. Get the yesterday date

export const yesterday: Date = ((d) => new Date(d.setDate(d.getDate() - 1)))(new Date());
// yesterday; // 2020-05-06
Enter fullscreen mode Exit fullscreen mode

22. Initialize the current date but set time to midnight

export const midnightOfToday = (): Date => new Date(new Date().setHours(0, 0, 0, 0));
// midnightOfToday(); // 2020-05-06T00:00:00.000Z
Enter fullscreen mode Exit fullscreen mode

23. Sort an array of dates

export const sortDescending = (arr: Date[]): Date[] => arr.sort((a, b) => a.getTime() - b.getTime());
// sortDescending([new Date('2020-01-01'), new Date('2020-01-02')]); // [new Date('2020-01-02'), new Date('2020-01-01')]
export const sortAscending = (arr: Date[]): Date[] => arr.sort((a, b) => b.getTime() - a.getTime());
// sortAscending([new Date('2020-01-01'), new Date('2020-01-02')]); // [new Date('2020-01-01'), new Date('2020-01-02')]
Enter fullscreen mode Exit fullscreen mode

Functions one liners ☝️


1. Box handler

export const boxHandler = (x: any): { next: (f: any) => any; done: (f: any) => any } => ({
  next: (f: any) => boxHandler(f(x)),
  done: (f: any) => f(x)
});
// const getMoney = (price) => Number.parseFloat(price.replace(/\$/, ''));
// const getPercent = (percent) => Number.parseFloat(percent.replace(/\%/)) * 0.01;

// const getDiscountPrice = (price, discount) =>
// boxHandler(getMoney(price))
// .done((cents) => boxHandler(getPercent(discount)).next((save) => cents - cents * save))
// .done((res) => res);

// getDiscountPrice('$6.00', '20%'); // 4.8
Enter fullscreen mode Exit fullscreen mode

2. Check if a value is a function

export const isFunction = (v: any): boolean =>
  ['[object Function]', '[object GeneratorFunction]', '[object AsyncFunction]', '[object Promise]'].includes(
    Object.prototype.toString.call(v)
  );
// isFunction(function () {}); // true
// isFunction(function* () {}); // true
// isFunction(async function () {}); // true
Enter fullscreen mode Exit fullscreen mode

3. Check if a value is a generator function

export const isGeneratorFunction = (v: any): boolean =>
  Object.prototype.toString.call(v) === '[object GeneratorFunction]';
// isGeneratorFunction(function () {}); // false
// isGeneratorFunction(function* () {}); // true
Enter fullscreen mode Exit fullscreen mode

4. Check if a value is an async function

export const isAsyncFunction = (v: any): boolean => Object.prototype.toString.call(v) === '[object AsyncFunction]';
// isAsyncFunction(function () {}); // false
// isAsyncFunction(function* () {}); // false
// isAsyncFunction(async function () {}); // true
Enter fullscreen mode Exit fullscreen mode

5. Compose functions from left to right

export const pipe =
  (...fns: any[]) =>
  (x: any) =>
    fns.reduce((y, f) => f(y), x);
// const lowercase = (str) => str.toLowerCase();
// const capitalize = (str) => `${str.charAt(0).toUpperCase()}${str.slice(1)}`;
// const reverse = (str) => str.split('').reverse().join('');

// const fn = pipe(lowercase, capitalize, reverse);

// We will execute `lowercase`, `capitalize` and `reverse` in order
// fn('Hello World') === 'dlrow olleH';
Enter fullscreen mode Exit fullscreen mode

6. Compose functions from right to left

export const compose =
  (...fns: any[]) =>
  (x: any) =>
    fns.reduceRight((y, f) => f(y), x);
// const lowercase = (str) => str.toLowerCase();
// const capitalize = (str) => `${str.charAt(0).toUpperCase()}${str.slice(1)}`;
// const reverse = (str) => str.split('').reverse().join('');

// const fn = compose(reverse, capitalize, lowercase);

// // We will execute `lowercase`, `capitalize` and `reverse` in order
// fn('Hello World') === 'dlrow olleH';
Enter fullscreen mode Exit fullscreen mode

7. Curry a function

export const curry = (fn: any, ...args: any[]): any =>
  fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
// const sum = (a, b, c) => a + b + c;
// curry(sum)(1)(2)(3); // 6
// curry(sum)(1, 2, 3); // 6
// curry(sum, 1)(2, 3); // 6
// curry(sum, 1)(2)(3); // 6
// curry(sum, 1, 2)(3); // 6
// curry(sum, 1, 2, 3); // 6
Enter fullscreen mode Exit fullscreen mode

8. Memoize a function

export const memoize = (fn: any) =>
  (
    (cache = Object.create(null)) =>
    (arg: any) =>
      cache[arg] || (cache[arg] = fn(arg))
  )();
// Calculate Fibonacci numbers
// const fibo = memoize((n: number) => (n <= 2 ? 1 : fibo(n - 1) + fibo(n - 2)));
// fibo(1); // 1
// fibo(2); // 1
// fibo(3); // 2
// fibo(4); // 3
// fibo(5); // 5
// fibo(6); // 8
Enter fullscreen mode Exit fullscreen mode

Math one liners ☝️


1. Calculate the angle of a line defined by two points

interface Point {
x: number;
y: number;
}

export const radiansAngle = (p1: Point, p2: Point): number => Math.atan2(p2.y - p1.y, p2.x - p1.x);
// radiansAngle({ x: 0, y: 0 }, { x: 0, y: 1 }); //
export const degreesAngle = (p1: Point, p2: Point): number => (Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180) / Math.PI;
// degreesAngle({ x: 0, y: 0 }, { x: 0, y: 3 }); // 90
Enter fullscreen mode Exit fullscreen mode

2. Calculate the distance between two points

interface Point {
  x: number;
  y: number;
}

export const distance = (p1: Point, p2: Point): number =>
  Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
// distance({ x: 0, y: 0 }, { x: 0, y: 1 }); // 1
Enter fullscreen mode Exit fullscreen mode

3. Calculate the linear interpolation between two numbers

export const lerp = (a: number, b: number, amount: number): number => (1 - amount) * a + amount * b;
// lerp(0, 1, 0.5); // 0.5
Enter fullscreen mode Exit fullscreen mode

4. Calculate the midpoint between two points

interface Point {
  x: number;
  y: number;
}

export const midpoint = (p1: Point, p2: Point): number[] => [(p1.x + p2.x) / 2, (p1.y + p2.y) / 2];
// midpoint({ x: 0, y: 0 }, { x: 0, y: 1 }); // [0, 0.5]
Enter fullscreen mode Exit fullscreen mode

5. Calculate the slope between two points

interface Point {
  x: number;
  y: number;
}

export const slope = (p1: Point, p2: Point): number => (p2.y - p1.y) / (p2.x - p1.x);
// slope({ x: 0, y: 0 }, { x: 0, y: 1 }); // 1
Enter fullscreen mode Exit fullscreen mode

6. Calculate the perpendicular slope between two points

interface Point {
  x: number;
  y: number;
}

export const perpendicularSlope = (p1: Point, p2: Point): number => -1 / slope(p1, p2);
// perpendicularSlope({ x: 0, y: 0 }, { x: 0, y: 1 }); // -1
Enter fullscreen mode Exit fullscreen mode

7. Check if a point is inside a rectangle

interface Point {
  x: number;
  y: number;
}

interface Rect {
  bottom: number;
  left: number;
  top: number;
  right: number;
}

export const isInside = (point: Point, rect: Rect): boolean =>
  point.x > rect.left && point.x < rect.right && point.y > rect.top && point.y < rect.bottom;

// isInside({ x: 0, y: 0 }, { left: 0, top: 0, right: 1, bottom: 1 }); // true
Enter fullscreen mode Exit fullscreen mode

8. Check if a point is inside a circle

interface Point {
  x: number;
  y: number;
}

export const isInsideCircle = (point: Point, center: Point, radius: number): boolean => {
  const distance = Math.sqrt(Math.pow(point.x - center.x, 2) + Math.pow(point.y - center.y, 2));
  return distance < radius;
};

// isInsideCircle({ x: 0, y: 0 }, { x: 0, y: 0 }, 1); // true
Enter fullscreen mode Exit fullscreen mode

9. Check if a rectangle contains other one

interface Rect {
  x1: number;
  x2: number;
  y1: number;
  y2: number;
}

export const contains = (a: Rect, b: Rect): boolean => a.x1 <= b.x1 && a.y1 <= b.y1 && a.x2 >= b.x2 && a.y2 >= b.y2;
// contains({ x1: 0, y1: 0, x2: 1, y2: 1 }, { x1: 0, y1: 0, x2: 1, y2: 1 }); // true
Enter fullscreen mode Exit fullscreen mode

10. Check if a rectangle overlaps another one

interface Rect {
  x1: number;
  x2: number;
  y1: number;
  y2: number;
}

export const overlaps = (a: Rect, b: Rect): boolean => !(a.x2 < b.x1 || a.x1 > b.x2 || a.y2 < b.y1 || a.y1 > b.y2);
// overlaps({ x1: 0, y1: 0, x2: 1, y2: 1 }, { x1: 0, y1: 0, x2: 1, y2: 1 }); // true
Enter fullscreen mode Exit fullscreen mode

11. Check if a rectangle is inside another one

interface Rect {
  x1: number;
  x2: number;
  y1: number;
  y2: number;
}

export const isInsideRect = (a: Rect, b: Rect): boolean => a.x1 >= b.x1 && a.y1 >= b.y1 && a.x2 <= b.x2 && a.y2 <= b.y2;
// isInsideRect({ x1: 0, y1: 0, x2: 1, y2: 1 }, { x1: 0, y1: 0, x2: 1, y2: 1 }); // true
Enter fullscreen mode Exit fullscreen mode

12. Check if a rectangle is outside another one

interface Rect {
  x1: number;
  x2: number;
  y1: number;
  y2: number;
}

export const isOutsideRect = (a: Rect, b: Rect): boolean =>
  a.x1 <= b.x1 || a.y1 <= b.y1 || a.x2 >= b.x2 || a.y2 >= b.y2;
// isOutsideRect({ x1: 0, y1: 0, x2: 1, y2: 1 }, { x1: 0, y1: 0, x2: 1, y2: 1 }); // true
Enter fullscreen mode Exit fullscreen mode

13. Check if a rectangle is touching another one

interface Rect {
  x1: number;
  x2: number;
  y1: number;
  y2: number;
}

export const isTouchingRect = (a: Rect, b: Rect): boolean =>
  a.x1 === b.x1 || a.x2 === b.x2 || a.y1 === b.y1 || a.y2 === b.y2;
// isTouchingRect({ x1: 0, y1: 0, x2: 1, y2: 1 }, { x1: 0, y1: 0, x2: 1, y2: 1 }); // true
Enter fullscreen mode Exit fullscreen mode

14. Convert degrees to radians

export const degsToRads = (deg: number): number => (deg * Math.PI) / 180.0;
// degsToRads(90); // 1.5707963267948966
Enter fullscreen mode Exit fullscreen mode

15. Convert radians to degrees

export const radsToDegs = (rad: number): number => (rad * 180.0) / Math.PI;
// radsToDegs(1.5707963267948966); // 90
Enter fullscreen mode Exit fullscreen mode

16. Normalize the ratio of a number in a range

export const normalizeRatio = (value: number, min: number, max: number): number => (value - min) / (max - min);
// normalizeRatio(0, 0, 1); // 0
Enter fullscreen mode Exit fullscreen mode

17. Round a number to the nearest multiple of a given value

export const roundNearest = (value: number, nearest: number): number => Math.round(value / nearest) * nearest;
// roundNearest(100, 30); // 90
// roundNearest(200, 30); // 210
// roundNearest(200, 40); // 200
Enter fullscreen mode Exit fullscreen mode

18. Round a number to a given number of decimal places

export const roundToDecimal = (value: number, decimals: number): number => {
const factor = Math.pow(10, decimals);
return Math.round(value * factor) / factor;
};
// roundToDecimal(1.2345, 2); // 1.23
Enter fullscreen mode Exit fullscreen mode

19. Calculate the average of arguments

export const average = (...args: number[]): number => args.reduce((a, b) => a + b) / args.length;
// average(1, 2, 3, 4); // 2.5
Enter fullscreen mode Exit fullscreen mode

20.Calculate the division of arguments

export const division = (...args: number[]): number => args.reduce((a, b) => a / b);
// division(1, 2, 3, 4); // 0.04166666666666666
Enter fullscreen mode Exit fullscreen mode

21. Calculate the factorial of a number

export const factorial = (n: number): number => (n <= 1 ? 1 : n * factorial(n - 1));
// factorial(2); // 2
// factorial(3); // 6
// factorial(4); // 24
// factorial(5); // 120
// factorial(6); // 720
Enter fullscreen mode Exit fullscreen mode

22. Calculate the mod of collection index

export const mod = (a: number, b: number): number => ((a % b) + b) % b;
// mod(-1, 5); // 4
// mod(3, 5); // 3
// mod(6, 5); // 1
Enter fullscreen mode Exit fullscreen mode

23. Calculate the remainder of division of arguments

export const remainder = (...args: number[]): number => args.reduce((a, b) => a % b);
// remainder(1, 2, 3, 4); // 1
Enter fullscreen mode Exit fullscreen mode

24. Calculate the sum of arguments

export const sum = (...args: number[]): number => args.reduce((a, b) => a + b);
// sum(1, 2, 3, 4); // 10
Enter fullscreen mode Exit fullscreen mode

25. Clamp a number between two values

export const clamp = (val: number, min: number = 0, max: number = 1): number => Math.max(min, Math.min(max, val));
// clamp(199, 10, 25); // 25
Enter fullscreen mode Exit fullscreen mode

26. Compute the greatest common divisor between two numbers

export const gcd = (a: number, b: number): number => (b === 0 ? a : gcd(b, a % b));
// gcd(10, 15); // 5
Enter fullscreen mode Exit fullscreen mode

27. Compute the least common multiple between two numbers

export const lcm = (a: number, b: number): number => (a * b) / gcd(a, b);
// lcm(10, 15); // 30
Enter fullscreen mode Exit fullscreen mode

28. Compute the median of a collection of numbers

export const median = (...args: number[]): number => {
  const sorted = args.sort((a, b) => a - b);
  const mid = Math.floor(sorted.length / 2);
  return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
};
// median(1, 2, 3, 4); // 2.5
Enter fullscreen mode Exit fullscreen mode

29. Multiply arguments

export const mul = (...args: number[]): number => args.reduce((a, b) => a * b);
// mul(1, 2, 3, 4); // 24
Enter fullscreen mode Exit fullscreen mode

30. Subtract arguments

export const subtract = (...args: number[]): number => args.reduce((a, b) => a - b);
// subtract(1, 2, 3, 4); // -8
Enter fullscreen mode Exit fullscreen mode

Misc one liners ☝️


1. Check if the code is running in Jest

export const isRunningInJest: boolean = typeof process !== 'undefined' && process.env.JEST_WORKER_ID !== undefined;
// isRunningInJest; // true
Enter fullscreen mode Exit fullscreen mode

2. Check if the code is running in NodeJS

export const isNode: boolean =
  typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
// isNode; // true
Enter fullscreen mode Exit fullscreen mode

3. Check if the code is running in the browser

export const isBrowser: boolean = typeof window === 'object' && typeof document === 'object';
// isBrowser; // true
Enter fullscreen mode Exit fullscreen mode

4. Clear all cookies

export const clearCookies = (): void =>
  document.cookie
    .split(';')
    .forEach(
      (c) => (document.cookie = c.replace(/^ +/, '').replace(/=.*/, `=;expires=${new Date().toUTCString()};path=/`))
    );
// clearCookies();
Enter fullscreen mode Exit fullscreen mode

5. Convert 3 digits color to 6 digits color

export const toFullHexColor = (color: string): string =>
  `#${(color.startsWith('#') ? color.slice(1) : color)
    .split('')
    .map((c) => `${c}${c}`)
    .join('')}`;
// toFullHexColor('123'); // '#112233'
// toFullHexColor('#123'); // '#112233'
// toFullHexColor('#abc'); // '#aabbcc'
Enter fullscreen mode Exit fullscreen mode

6. Convert Celsius to Fahrenheit

export const celsiusToFahrenheit = (celsius: number): number => (celsius * 9) / 5 + 32;
// celsiusToFahrenheit(15); // 59
// celsiusToFahrenheit(0); // 32
// celsiusToFahrenheit(-20); // -4
Enter fullscreen mode Exit fullscreen mode

7. Convert Fahrenheit to Celsius

export const fahrenheitToCelsius = (fahrenheit: number): number => ((fahrenheit - 32) * 5) / 9;
// fahrenheitToCelsius(59); // 15
Enter fullscreen mode Exit fullscreen mode

8. Convert Celsius to Kelvin

export const celsiusToKelvin = (celsius: number): number => celsius + 273.15;
// celsiusToKelvin(15); // 298.15
// celsiusToKelvin(0); // 273.15
Enter fullscreen mode Exit fullscreen mode

9. Convert Fahrenheit to Kelvin

export const fahrenheitToKelvin = (fahrenheit: number): number => ((fahrenheit - 32) * 5) / 9 + 273.15;
// fahrenheitToKelvin(59); // 298.15
Enter fullscreen mode Exit fullscreen mode

10. Convert Kelvin to Celsius

export const kelvinToCelsius = (kelvin: number): number => kelvin - 273.15;
// kelvinToCelsius(298.15); // 15
// kelvinToCelsius(273.15); // 0
Enter fullscreen mode Exit fullscreen mode

11. Convert Kelvin to Fahrenheit

export const kelvinToFahrenheit = (kelvin: number): number => (kelvin * 9) / 5 - 459.67;
// kelvinToFahrenheit(298.15); // 59
// kelvinToFahrenheit(273.15); // 32
Enter fullscreen mode Exit fullscreen mode

12. Convert rgb color to hex

export const rgbToHex = (red: number, green: number, blue: number): string =>
  `#${[red, green, blue].map((v) => v.toString(16).padStart(2, '0')).join('')}`;
// rgbToHex(0, 255, 255); // '#00ffff'
Enter fullscreen mode Exit fullscreen mode

13. Convert hex color to rgb

export const hexToRgb = (hex: string): [number, number, number] => {
  const [r, g, b] = hex
    .slice(1)
    .split('')
    .map((c) => parseInt(c + c, 16));
  return [r, g, b];
}; // hexToRgb('#00ffff'); // [0, 255, 255]

// Convert URL parameters to object
export const getUrlParams = (query: string): Record<string, string> =>
  Array.from(new URLSearchParams(query)).reduce(
    (p, [k, v]) => Object.assign({}, p, { [k]: p[k] ? (Array.isArray(p[k]) ? p[k] : [p[k]]).concat(v) : v }),
    {} as Record<string, string>
  );
// getUrlParams(location.search); // Get the parameters of the current URL
// getUrlParams('foo=Foo&bar=Bar'); // { foo: "Foo", bar: "Bar" }
// Duplicate key
// getUrlParams('foo=Foo&foo=Fuzz&bar=Bar'); // { foo: ["Foo", "Fuzz"], bar: "Bar" }
Enter fullscreen mode Exit fullscreen mode

14. Convert object to URL parameters

export const toUrlParams = (obj: Record<string, string | string[]>): string => {
  const params = new URLSearchParams();
  Object.entries(obj).forEach(([k, v]) => {
    if (Array.isArray(v)) {
      v.forEach((val) => params.append(k, val));
    } else {
      params.append(k, v);
    }
  }),
    params.toString();
  return params.toString();
};
// toUrlParams({ foo: "Foo", bar: "Bar" }); // "foo=Foo&bar=Bar"
Enter fullscreen mode Exit fullscreen mode

15. Decode a JWT token

export const decodeJwt = (token: string): Record<string, string> => {
  const [, payload] = token.split('.');
  return JSON.parse(atob(payload));
};
// decodeJwt('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c');
// { sub: "1234567890", name: "John Doe", iat: 1516239022 }
Enter fullscreen mode Exit fullscreen mode

16. Encode a JWT token

export const encodeJwt = (obj: Record<string, string>): string => {
  const payload = JSON.stringify(obj);
  const base64Payload = btoa(payload);
  const base64Header = btoa(JSON.stringify({ alg: 'none', typ: 'JWT' }));
  return `${base64Header}.${base64Payload}`;
};
// encodeJwt({ sub: "1234567890", name: "John Doe", iat: 1516239022 });
// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ"
Enter fullscreen mode Exit fullscreen mode

17. Detect dark mode

export const isDarkMode: boolean = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
// isDarkMode; // true
Enter fullscreen mode Exit fullscreen mode

18. Easing functions

export const linear = (t: number): number => t;

export const easeInQuad = (t: number): number => t * t;
export const easeOutQuad = (t: number): number => t * (2 - t);
export const easeInOutQuad = (t: number): number => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t);

export const easeInCubic = (t: number): number => t * t * t;
export const easeOutCubic = (t: number): number => --t * t * t + 1;
export const easeInOutCubic = (t: number): number =>
  t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;

export const easeInQuart = (t: number): number => t * t * t * t;
export const easeOutQuart = (t: number): number => 1 - --t * t * t * t;
export const easeInOutQuart = (t: number): number => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t);

export const easeInQuint = (t: number): number => t * t * t * t * t;
export const easeOutQuint = (t: number): number => 1 + --t * t * t * t * t;
export const easeInOutQuint = (t: number): number => (t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t);

export const easeInSine = (t: number): number => 1 + Math.sin((Math.PI / 2) * t - Math.PI / 2);
export const easeOutSine = (t: number): number => Math.sin((Math.PI / 2) * t);
export const easeInOutSine = (t: number): number => (1 + Math.sin(Math.PI * t - Math.PI / 2)) / 2;

export const easeInElastic = (t: number): number => (0.04 - 0.04 / t) * Math.sin(25 * t) + 1;
export const easeOutElastic = (t: number): number => ((0.04 * t) / --t) * Math.sin(25 * t);
export const easeInOutElastic = (t: number): number =>
  (t -= 0.5) < 0 ? (0.02 + 0.01 / t) * Math.sin(50 * t) : (0.02 - 0.01 / t) * Math.sin(50 * t) + 1;
Enter fullscreen mode Exit fullscreen mode

19. Emulate a dice throw

export const throwdice = (): number => ~~(Math.random() * 6) + 1;
// throwdice(); // 4
// throwdice(); // 1
// throwdice(); // 6
Enter fullscreen mode Exit fullscreen mode

20. Emulate a coin flip

export const flipcoin = (): boolean => Math.random() < 0.5;
// flipcoin(); // true
Enter fullscreen mode Exit fullscreen mode

21. Encode a URL

export const encode = (url: string): string =>
  encodeURIComponent(url)
    .replace(/!/g, '%21')
    .replace(/~/g, '%7E')
    .replace(/\*/g, '%2A')
    .replace(/'/g, '%27')
    .replace(/\(/g, '%28')
    .replace(/\)/g, '%29')
    .replace(/%20/g, '+');
// encode('https://www.google.com/'); // 'https%3A%2F%2Fwww.google.com%2F'
Enter fullscreen mode Exit fullscreen mode

22. Decode a URL

export const decode = (url: string): string => decodeURIComponent(url.replace(/\+/g, '%20'));
// decode('https%3A%2F%2Fwww.google.com%2F'); // 'https://www.google.com/'
Enter fullscreen mode Exit fullscreen mode

23. Get the current URL

export const getUrl = (): string => window.location.href;
// getUrl(); // 'https://www.google.com/'
Enter fullscreen mode Exit fullscreen mode

24. Get the first defined and non null argument

export const coalesce = (...args: any[]): any[] => args.find((item) => ![undefined, null].includes(item));
// coalesce(undefined, null, 'helloworld', NaN); // 'helloworld'
Enter fullscreen mode Exit fullscreen mode

25. Get the value of a param from a URL

export const getParam = (url: string, param: string): string | null =>
  new URLSearchParams(new URL(url).search).get(param);
getParam('http://domain.com?message=hello', 'message'); // 'hello'
Enter fullscreen mode Exit fullscreen mode

26. Get type of a variable in string

export const getTypeOf = (obj: any): string => (Object.prototype.toString.call(obj).match(/\[object (.*)\]/) as string[])[1];
// getTypeOf('hello world'); // String
// getTypeOf(1000); // Number
// getTypeOf(Infinity); // Number
// getTypeOf(true); // Boolean
// getTypeOf(Symbol()); // Symbol
// getTypeOf(null); // Null
// getTypeOf(undefined); // Undefined
// getTypeOf({}); // Object
// getTypeOf([]); // Array
// getTypeOf(/[a-z]/g); // RegExp
// getTypeOf(new Date(2021)); // Date
// getTypeOf(new Error()); // Error
// getTypeOf(function () {}); // Function
// getTypeOf((a, b) => a + b); // Function
// getTypeOf(async () => {}); // AsyncFunction
// getTypeOf(document); // HTMLDocument
Enter fullscreen mode Exit fullscreen mode

27. Redirect the page to HTTPS if it is in HTTP

export const redirectHttps = (): string => (location.protocol === 'https:' ? '' : (location.protocol = 'https:'));
Enter fullscreen mode Exit fullscreen mode

28. Run Promises in sequence

export const run = (promises: Promise<any>[]): Promise<any> =>
  promises.reduce((p, c) => p.then((rp) => c.then((rc) => [...rp, rc])), Promise.resolve([]));
// run(promises).then((results) => {
// // `results` is an array of promise results in the same order
// });
Enter fullscreen mode Exit fullscreen mode

29. Run Promises in parallel

export const runParallel = (promises: Promise<any>[]): Promise<any> => Promise.all(promises);
// runParallel(promises).then((results) => {
// // `results` is an array of promise results in the same order
// });
Enter fullscreen mode Exit fullscreen mode

30. Run Promises in parallel and return the results in the same order

export const runParallelOrder = (promises: Promise<any>[]): Promise<any> =>
  Promise.all(promises).then((results) => results.reduce((p, c) => [...p, c], []));
// runParallelOrder(promises).then((results) => {
// // `results` is an array of promise results in the same order
// });
Enter fullscreen mode Exit fullscreen mode

31. Wait for an amount of time

export const wait = async (milliseconds: number) => new Promise((resolve) => setTimeout(resolve, milliseconds));
// wait(1000).then(() => console.log('done'));
Enter fullscreen mode Exit fullscreen mode

32. Add an ordinal suffix to a number

export const addOrdinal = (n: number): string => `${n}${[, 'st', 'nd', 'rd'][(n % 100 >> 3) ^ 1 && n % 10] || 'th'}`;
// addOrdinal(1); // '1st'
// addOrdinal(2); // '2nd'
// addOrdinal(3); // '3rd'
// addOrdinal(11); // '11th'
// addOrdinal(12); // '13th'
// addOrdinal(13); // '13th'
Enter fullscreen mode Exit fullscreen mode

33. Convert a number to equivalent characters

export const toChars = (n: number): string =>
  `${n >= 26 ? toChars(Math.floor(n / 26) - 1) : ''}${'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[n % 26]}`;
// toChars(0); // A
// toChars(1); // B
// toChars(25); // Z

// toChars(26); // AA
// toChars(27); // AB
// toChars(51); // AZ
Enter fullscreen mode Exit fullscreen mode

Objects one liners ☝️


1.Check if multiple objects are equal

export const isEqual = (...objects: object[]): boolean =>
  objects.every((obj) => JSON.stringify(obj) === JSON.stringify(objects[0]));
// isEqual({ foo: 'bar' }, { foo: 'bar' }); // true
// isEqual({ foo: 'bar' }, { bar: 'foo' }); // false
Enter fullscreen mode Exit fullscreen mode

2. Extract values of a property from an array of objects

export const pluck = (objs: any[], property: any) => objs.map((obj) => obj[property]);
// pluck(
// [
// { name: 'John', age: 20 },
// { name: 'Smith', age: 25 },
// { name: 'Peter', age: 30 }
// ],
// 'name'
// ); // ['John', 'Smith', 'Peter']
Enter fullscreen mode Exit fullscreen mode

3. Get the value at given path of an object

export const getValue = (path: string, obj: any) => path.split('.').reduce((acc, c) => acc && acc[c], obj);
// getValue('a.b', { a: { b: 'Hello World' } }); // 'Hello World';
Enter fullscreen mode Exit fullscreen mode

4. Remove all null and undefined properties from an object

export const removeNullUndefined = (obj: Object) =>
  Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));
// removeNullUndefined({
// foo: null,
// bar: undefined,
// fuzz: 42,
// }); // { fuzz: 42 }
Enter fullscreen mode Exit fullscreen mode

5. Shallow clone an object

export const shallowCopy = (obj: Object): Object => ({ ...obj });
Enter fullscreen mode Exit fullscreen mode

6. Sort an object by its properties

export const sort = (obj: any) =>
  Object.keys(obj)
    .sort()
    .reduce((p: any, c: string) => ((p[c] = obj[c]), p), {});
// const colors = {
// white: '#ffffff',
// black: '#000000',
// red: '#ff0000',
// green: '#008000',
// blue: '#0000ff',
// };
// sort(colors);
// {
// black: '#000000',
// blue: '#0000ff',
// green: '#008000',
// red: '#ff0000',
// white: '#ffffff',
// }
Enter fullscreen mode Exit fullscreen mode

String one liners ☝️


1. Capitalize a string

export const capitalize = (str: string): string => `${str.charAt(0).toUpperCase()}${str.slice(1)}`;
// capitalize('hello world'); // 'Hello world'
Enter fullscreen mode Exit fullscreen mode

2. Check if a path is relative

export const isRelative = (path: string): boolean => !/^([a-z]+:)?[\\/]/i.test(path);
// isRelative('/foo/bar/baz'); // false
// isRelative('C:\\foo\\bar\\baz'); // false
// isRelative('foo/bar/baz.txt'); // true
// isRelative('foo.md'); // true
Enter fullscreen mode Exit fullscreen mode

3. Check if a string consists of a repeated character sequence

export const consistsRepeatedSubstring = (str: string): boolean => `${str}${str}`.indexOf(str, 1) !== str.length;
// consistsRepeatedSubstring('aa'); // true
// consistsRepeatedSubstring('aaa'); // true
// consistsRepeatedSubstring('ababab'); // true
// consistsRepeatedSubstring('abc'); // false
Enter fullscreen mode Exit fullscreen mode

4. Check if a URL is absolute

export const isAbsoluteUrl = (url: string): boolean => /^[a-z][a-z0-9+.-]*:/.test(url);
// isAbsoluteUrl('https://1loc.dev'); // true
// isAbsoluteUrl('https://1loc.dev/foo/bar'); // true
// isAbsoluteUrl('1loc.dev'); // false
// isAbsoluteUrl('//1loc.dev'); // false
Enter fullscreen mode Exit fullscreen mode

5. Check if two strings are anagram

export const areAnagram = (str1: string, str2: string): boolean =>
  str1.toLowerCase().split('').sort().join('') === str2.toLowerCase().split('').sort().join('');
// areAnagram('listen', 'silent'); // true
// areAnagram('they see', 'the eyes'); // true
// areAnagram('node', 'deno'); // true
Enter fullscreen mode Exit fullscreen mode

6. Convert a base64 encoded string to an uint8 array

export const base64ToUint8 = (str: string): Uint8Array => Uint8Array.from(atob(str), (c) => c.charCodeAt(0));
// base64ToUint8('SGVsbG8gV29ybGQ='); // Uint8Array [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
Enter fullscreen mode Exit fullscreen mode

7. Convert a letter to associate emoji

export const letterToEmoji = (c: string): string => String.fromCodePoint(c.toLowerCase().charCodeAt(0) + 127365);
// letterToEmoji('a'); // πŸ‡¦
// letterToEmoji('b'); // πŸ‡§
Enter fullscreen mode Exit fullscreen mode

8. Convert a string to camelCase

export const toCamelCase = (str: string): string =>
str.trim().replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''));
// toCamelCase('background-color'); // backgroundColor
// toCamelCase('-webkit-scrollbar-thumb'); // WebkitScrollbarThumb
// toCamelCase('_hello_world'); // HelloWorld
// toCamelCase('hello_world'); // helloWorld
Enter fullscreen mode Exit fullscreen mode

9. Convert a string to PascalCase

export const toPascalCase = (str: string): string =>
  (str.match(/[a-zA-Z0-9]+/g) || []).map((w) => `${w.charAt(0).toUpperCase()}${w.slice(1)}`).join('');
// toPascalCase('hello world'); // 'HelloWorld'
// toPascalCase('hello.world'); // 'HelloWorld'
// toPascalCase('foo_bar-baz'); // FooBarBaz
Enter fullscreen mode Exit fullscreen mode

10. Convert a string to URL slug

export const slugify = (str: string): string =>
  str
    .toLowerCase()
    .replace(/\s+/g, '-')
    .replace(/[^\w-]+/g, '');
// slugify('Chapter One: Once upon a time...'); // 'chapter-one-once-upon-a-time'
Enter fullscreen mode Exit fullscreen mode

11. Convert a Windows file path to Unix path

export const toUnixPath = (path: string): string => path.replace(/[\\/]+/g, '/').replace(/^([a-zA-Z]+:|\.\/)/, '');
// toUnixPath('./foo/bar/baz'); // foo/bar/baz
// toUnixPath('C:\\foo\\bar\\baz'); // /foo/bar/baz
Enter fullscreen mode Exit fullscreen mode

12. Convert an uint8 array to a base64 encoded string

export const uint8ToBase64 = (arr: Uint8Array): string => Buffer.from(arr).toString('base64');
// uint8ToBase64(Uint8Array.from([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100])); // 'SGVsbG8gV29ybGQ='
Enter fullscreen mode Exit fullscreen mode

13. Convert camelCase to kebab-case and vice versa

export const kebabToCamel = (str: string): string => str.replace(/-./g, (m) => m.toUpperCase()[1]);
// kebabToCamel('background-color'); // 'backgroundColor'

export const camelToKebab = (str: string): string => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
// camelToKebab('backgroundColor'); // 'background-color'
Enter fullscreen mode Exit fullscreen mode

14. Convert snake_case to camelCase

export const snakeToCamel = (str: string): string =>
  str.toLowerCase().replace(/(_\w)/g, (m) => m.toUpperCase().substr(1));
// snakeToCamel('HELLO_world'); // 'helloWorld'
Enter fullscreen mode Exit fullscreen mode

15. Count the occurrences of a character in a string

export const countOccurrences = (str: string, char: string): number => [...str].filter((item) => item === char).length;
// countOccurrences('a.b.c.d.e', '.'); // 4
Enter fullscreen mode Exit fullscreen mode

16. Format a string

export const format = (str: string, ...vals: string[]): string =>
  vals.reduce((s, v, i) => s.replace(new RegExp('\\{' + i + '\\}', 'g'), v), str);
// const template = 'My name is {0} and I am {1} years old';

// format(template, 'John', '30');
// // My name is John and I am 30 years old

// format(template, 'Jane', '20');
// // My name is Jane and I am 20 years old
Enter fullscreen mode Exit fullscreen mode

17. Generate a hash of a string

export const hash = (str: string): number =>
  str.split('').reduce((prev, curr) => (Math.imul(31, prev) + curr.charCodeAt(0)) | 0, 0);
// hash('hello'); // 99162322
Enter fullscreen mode Exit fullscreen mode

18. Get the base URL without any parameters

export const baseUrl = (url: string): string => url.split('?')[0];
// baseUrl('https://domain.com/path/sub/path?foo=bar&hello=world'); // 'https://domain.com/path/sub/path'
Enter fullscreen mode Exit fullscreen mode

19. Get the number of a character in a string

export const characterCount = (str: string, char: string): number => str.split(char).length - 1;
// characterCount('192.168.1.1', '.'); // 3
// characterCount('star wars', 's'); // 2
Enter fullscreen mode Exit fullscreen mode

20. Replace the first given number of characters of a string with another character

export const mask = (str: string, num: number, mask: string): string =>
  `${str}`.slice(num).padStart(`${str}`.length, mask);
// mask(1234567890, 3, '*'); // ***4567890
Enter fullscreen mode Exit fullscreen mode

21. Uppercase the first character of each word in a string

export const uppercaseWords = (str: string): string => str.replace(/^(.)|\s+(.)/g, (c) => c.toUpperCase());
// uppercaseWords('hello world'); // 'Hello World'
Enter fullscreen mode Exit fullscreen mode

Top comments (16)

Collapse
joelbonetr profile image
JoelBonetR • Edited on

Hi Christian,

As advice, take care when using TS. It sets you in a peace of mind state, thinking that you'll be available to set only the types you defined for each property but that's not how it works.

If you set

export const isEmpty = <T, \_>(arr: T[]): boolean => Array.isArray(arr) && !arr.length;
Enter fullscreen mode Exit fullscreen mode

in a package, lib or js SDK, other software will still be capable of sending 'non expected' data into it and it's not protected or workarounded to ensure the reliability:

The transpiled to JS version will look like that:

export const isEmpty = (arr) => Array.isArray(arr) && !arr.length;
Enter fullscreen mode Exit fullscreen mode

in which case:

isEmpty();  // false
isEmpty('') // false
isEmpty(null) // false
isEmpty(undefined) // false
Enter fullscreen mode Exit fullscreen mode

The reason is that Array.isArray(arr) evaluates into false thus returning false directly and the short circuit AND operator is not evaluated.

Some cases like that leads you to avoid one-liners for good:

const isEmpty = (arr) => {
    if(Array.isArray(arr)) return !arr.length 
    else throw `isEmpty error, Array expected but found ${typeof arr}`;
}
Enter fullscreen mode Exit fullscreen mode

Now you can translate that into TS if you want but it just doesn't matter, either it beign JS or TS, it will be reliable and not depend on the target devs to use TS as well (which of course still won't cover API responses and so unless you specificaly typecheck them etc...).

Cheers!

Collapse
lukeshiru profile image
Luke Shiru
  1. I prefer his isEmpty because I will not have to wrap it in a try/catch like yours.
  2. The idea of using TypeScript is that folks will get an error in dev time when trying to pass something to isEmpty that isn't an array, so they'll not be able to do isEmpty() or isEmpty(null) because they'll get compilation errors.
  3. Even if the people using your lib is not using TS, they still get the type checking benefits (red underline when they pass something that isn't valid).
  4. The compiled code will be unnecessarily longer with that throw on it. Not to mention the error isn't localized as a type error could be on the IDE.
  5. If you still think is a better option to throw, then why are you even using TypeScript?

Don't get me wrong, I still don't think the isEmpty function is perfect, it could use a rename, and better generics:

export const isEmptyArray = <Item>({ length }: ArrayLike<Item>) => length > 0;
Enter fullscreen mode Exit fullscreen mode

But I definitely prefer the author function to the throwing one you propose.

Collapse
joelbonetr profile image
JoelBonetR • Edited on

Hi Luke,
I'll try to answer your questions or points one by one below

I prefer his isEmpty because I will not have to wrap it in a try/catch like yours

it is not a try catch, it is a throw, which is meant to articulate user-defined exceptions.

It is used as "offensive programming" tool and it should be used whenever defensive programming doesn't fit in the specific use-case.

Read more about defensive and offensive software design.
A bit excerpt from that:

Generally speaking, it is preferable to throw exception messages that enforce part of your API contract and guide the developer instead of returning error code values that do not point to where the exception occurred or what the program stack looked liked, Better logging and exception handling will increase robustness and security of your software, while minimizing developer stress.

As extra info, a try...catch statement will only reach the catch if any Exception occurred inside the try execution block. It can be either a language pre-defined exception or a user-defined one (using throw).

You should use try...catch when calling functions that throw errors and maybe using the finally optional statement to ensure defensive programming if it suits.

Example:

  /**
   * Selects a row from User model by primary key
   * @param {number} userId 
   * @returns {Array<any>}
   */
  const getUserData = (userId) => {
    if (typeof userId !== number) throw `getUserData Error. Number expected but found ${typeof userId}`;

    return await Users.find({id: userId});
  };

  /**
   * Gets information about a given user by ID
   * @param {number} userId
   * @returns {Array<any>}
   */
  const getUserInformation = (userId) => {
    let info = [];
    try {
      info = getUserData(userId);
    } catch (err) {
      console.error(`getUserInformation Error. ${err.message} in ${err.stack}`);
    } finally {
      return info;
    }
  };
Enter fullscreen mode Exit fullscreen mode

You'll notice that getUserData handles it's own exceptions (as it should be) and that, the caller ( getUserInformation in this case) has a try...catch.
If you send a string into it, it will try to call getUserData with this string and getUserData will throw an exception to the caller, that will be captured by the catch statement.

In this case we apply both offensive and defensive programming designs. Notice that getUserInformation will always return an Array, either be empty or filled in with the data of that given user from the DB.

In this case it may be useful to use

const userControllerExample = (user) => {
  if (isEmptyArray( getUserInfo( user.id ) ) ) throw `userControllerExample Error. No data was found searching for user ${user.id}`;
}
Enter fullscreen mode Exit fullscreen mode

and so on.

Hope now you find it as useful as it is IRL.

The idea of using TypeScript is that folks will get an error in dev time when trying to pass something to isEmpty that isn't an array, so they'll not be able to do isEmpty() or isEmpty(null) because they'll get compilation errors.
Even if the people using your lib is not using TS, they still get the type checking benefits (red underline when they pass something that isn't valid).

You may need several manual hard work to reach that, specially when dealing with different microservices.

If you receive a string or a undefined on a property after an API Call (in runtime, of course) when you expect an Array, either be by any mistake on the other side or by lack of data, and you try to pass isEmpty( myResponse.info ) you may get one of those:

isEmpty(undefined) // false
isEmpty('') // false
isEmpty(null) // false
Enter fullscreen mode Exit fullscreen mode

And the purpose of the function isEmpty() is now missleading and useless, sending the issue to the next function in the stack:

if( !isEmpty( myResponse.info ) getPreferences( myResponse.info );
Enter fullscreen mode Exit fullscreen mode

So if myResponse.info is undefined, an empty string, null... you will get false, thus send this "allegedly non-empty value" to getPreferences, in which you'll get a weird runtime error like that Uncaught TypeError: Cannot read properties of undefined.

If you're objective here, you'll see that the isEmpty function is not exactly doing what anyone would assume it does.

The compiled code will be unnecessarily longer with that throw on it. Not to mention the error isn't localized as a type error could be on the IDE.

It's the responsibility of the engine to handle that and optimize it, not ours. As devs we need to ensure there are no errors in runtime as well and, in case any bug appear, we should provide the tools to find the origin of the issue as fast as possible.

Don't build software thinking on how the engine will interpret and handle it. There are good reasons for that:
1- There are more than a single JS engine (V8, Spidermonkey, webkit) and there are differences as well between V8 in Node than V8 in Deno to set some examples.
2- It may change at any time without notifying you. Any design flaw, optimization path or performance increase the maintainers of each engine find during their job will be prioritized, developed, merged and set into production and they may or may not set the details publicly.

If you still think is a better option to throw, then why are you even using TypeScript?

You should also use Throw along TS. Check the advanced types reference, specifically type guards.

I'm copying the example in TS doc for convenience:

function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
    return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}
Enter fullscreen mode Exit fullscreen mode

It's just that in this specific use-case you'll be good either with TS or without it. You can also set JSDoc instead:

  /**
   * Checks whether an Array is empty or not
   * @param {Array<any>} arr
   * @returns {boolean}
   */
  const isEmptyArray = (arr) => {
    if (Array.isArray(arr)) return !arr.length;
    else throw `isEmpty error, Array expected but found ${typeof arr}`;
  };
Enter fullscreen mode Exit fullscreen mode

And the result will be the same adding compatibility with any TS project.

Don't get me wrong, I still don't think the isEmpty function is perfect, it could use a rename, and better generics:
export const isEmptyArray = ({ length }: ArrayLike) => length > 0;

This won't prevent it to evaluate undefined.length and throw Uncaught TypeError: Cannot destructure property 'length' of 'undefined' as it is undefined.

We got plenty of tools in programming language APIs and the major part of them implement try...catch and exceptions for good reasons (except from some "low level" languages such C in which there are no Exceptions, if you're curious about that I'm letting a paragraph below).

The first step is to learn the tools, then discern whether to use them and then use them. You'll become a better developer each time you learn something new during this process. 😁


In C, the errors are notified by the returned value of the function, the exit value of the process, signals to the process (Program Error Signals (GNU libc)) or the CPU hardware interruption (or other notification error form the CPU if any), for example, see How processor handles the case of division by zero. You still can handle some sort of exception-like manually using setjmp.h.

Thread Thread
lukeshiru profile image
Luke Shiru • Edited on

it is not a try catch, it is a throw, which is meant to articulate user-defined exceptions.

It is used as "offensive programming" tool and it should be used whenever defensive programming doesn't fit in the specific use-case.

I know you used a throw, my point is that in order to use your function, now I need to do this:

try {
    isEmpty(value);
} catch (error) {
    console.error(error);
}
Enter fullscreen mode Exit fullscreen mode

Every time you write a throw in your code, you're just saying: If they don't handle the error, make their app explode. Which is far from ideal.

If you receive a string or a undefined on a property after an API Call (in runtime, of course) when you expect an Array, either be by any mistake on the other side or by lack of data, and you try to pass isEmpty( myResponse.info ) you may get one of those:

This is a very common misconception that I had to cover in a post already (your last point about C goes into the same argument), but validations and type checking are two different concerns, one is related to runtime and the other is related to development. If you'll use isEmpty with something you don't know the type of, you should do a validation before and that should be typed correctly. Let's say your API is really bad and can return either an array, or a string with an error, then you should check if is an array before working with it, and the response should be typed as string | Array<Type> (you can use tools like Zod to make this far easier).

Not to mention that if you have code as Array.isArray inside an isEmpty function, then that function isn't actually just checking if the array is "empty", it is doing a type validation and then that check, so is doing more than it should.

It's the responsibility of the engine to handle that and optimize it, not ours. As devs we need to ensure there are no errors in runtime as well and, in case any bug appear, we should provide the tools to find the origin of the issue as fast as possible.

Even minified...

// This:

const i=({length})=>length>0

// Is better than this:
const isEmpty=a=>{if(Array.isArray(a))return!a.length;throw`isEmpty error, Array expected but found ${typeof a}`}
Enter fullscreen mode Exit fullscreen mode

So no, is not the responsibility of the engine, is yours as a developer.

This won't prevent it to evaluate undefined.length and throw Uncaught TypeError: Cannot destructure property 'length' of 'undefined' as it is undefined.

If you're working with TS and validations as I mentioned above, yes it will ... and even if it didn't, you're making an argument for me because I didn't had to write the exception, it was made for me automatically.


The only folks I know that like code that throws work with Java, everyone else I know hate code that throws. If we have a type system, we can use that to handle edge cases way before we reach them in prod (which ideally should never happen in the first time).

In October this year I'll become a "10 year TS developer", I used it since it first public release in 2012 and I fell in love with it. And if that is worth anything to you as "experience in TS", then trust me when I say that if you write TS code that throws, then you're just not making good use of TS.

Thread Thread
joelbonetr profile image
JoelBonetR • Edited on

I agree with almost everything you said except for the usefullness of controlled errors.
I've updated the comment before to add an example on how it should be used.

It is better to control which errors can happen (not necessarily type errors) and being able to defensively control what the function will return so the software execution doesn't stop by that error, than having your software broke in production for some minutes because "cannot read property x from undefined" or things like that.

The only folks I know that like code that throws work with Java. Everyone else I know hate code that throws...

It's not a matter of like or dislike, those are tools that the language implements and are usefull in every single software you may write. Disliking them won't make them less usefull πŸ€·β€β™‚οΈ

Error recovery is an important point when giving service to customers and/or third parties (basically always). throw along with try...catch gives you a more detailed information about what went wront, where it happened and the data passing through that specific point that caused the exception. Thus providing shorter debug times, faster hotfixing, faster new devs handover, lets you to register meaningful logs, increases the reliability of the software and makes you curstomers happier about the product by the use of finally or other defensive programming designs so they can keep working with the rest when a feature is broken.

Has it ever happened to you that you are playing some videogame and it suddenly breaks to an unhandled exception? Specially in this situation where you forget to save your game for about an hour... This is how your customers feel when the software breaks to an unhandled error when working/using it.

In such situation either you blame the game or the publisher (if you don't have software dev skills) or you blame that moron dev that forget to scope it's code inside a try...catch and set a recovery path for that error.
When we are developing, that moron could be us if we forget about that important part πŸ˜…

Thread Thread
lukeshiru profile image
Luke Shiru

But I'm not saying you shouldn't handle errors, I'm saying you shouldn't throw them. There's a difference πŸ˜… ... a few months ago I twitted about this same topic and a friend of mine that mainly works with C++ asked about handling exceptions, and we had this beautiful meme response of a friend of his:

Screenshot of a tweet in which I wrote that I don't like code that throws, a friend ansked "hod do you deal with exceptions?" and a friend of his answer with the meme "that's the neat part, you don't"

But basically I'm not saying you shouldn't handle errors, I'm just saying you shouldn't throw (the try/catch flow is horrible), and instead you should rely on better coding practices, such as writing code that doesn't fail. This is a very "FP" approach, but the idea is that you have types to represent the possible values that a function returns, including a type when everything goes wrong (a pretty common one is the Maybe type). A good example is NaN in JavaScript, which is a type when something with numbers didn't go as planned that you get instead of throwing ^_^

Thread Thread
joelbonetr profile image
JoelBonetR • Edited on

Hahaha gosh it's the first time I read anyone mentioning this in this community.

While this could be a good approach (it is mentioned in my comment before about defensive programming on a different way if you check the link) if the language itself is not applying this "way to work" it will eventually fail (mostly by human error, like the major part if not all bugs we encounter).

It has nothing to do with FP and it's more about the implementation of a given language and how you code with it.

I like defensive programming a lot, still I'm mainly a JS dev (since some years ago) and I'm aware that the language is not implemented like Elm. I'm also aware that I've juniors in the team I manage on almost every project and I need to set up realistic boundaries for them to code in.

Even you code in that way, you can face an error pretty easily when dealing with the own language API as the pre-defined methods of the language will throw errors for sure that, if not controlled, will break your software in runtime.

It is what it is, not what we would like to have πŸ€·β€β™‚οΈπŸ˜…πŸ˜‚

Thread Thread
lukeshiru profile image
Luke Shiru • Edited on

Juniors are one of the main reasons I avoid code that throws. If not they need to write try/catch blocks for everything, and if they forget, then the app will "explode" which isn't great. throw is basically making your functions impure with no good reason. And yes, JS has API's that throws, but that doesn't mean you should throw as well, that only means that you should only handle throw when you don't have control over that, not to mention that you can easily wrap those native functions and methods and just make your wrappers "eat" that throw. For example:

const jsonParse<Output = unknown> = (value: string) => {
  try {
    return JSON.parse(value) as Output;
  } catch {
    return undefined;
  }
};

jsonParse("{}"); // {}
jsonParse("invalid"); // undefined

// So instead of try catching everywhere, you can simply combine it with ?? like this:

const example = jsonParse<Example>(value) ?? defaultExample;
Enter fullscreen mode Exit fullscreen mode

When I wrote the article saying that we as devs don't need null in JS, one of the many arguments I receive in the comments was similar to yours: "But, the language has that and it uses it in some places". The thing is that's not excuse to use it yourself. Just because JS has that "bad practice", it doesn't mean you need to have it as well (which should be pretty clear, when one of the most popular JS books is called "JavaScript: The Good Parts").

JS still has eval on it, but you wouldn't use it because you know is bad.

We will not change our minds, but I know I worked for a few years with code that throws and since I learn I didn't need that, I stopped doing it (and I'm a happier developer thanks to that, and my co-workers as well). I know "both sides" of this argument, now my question is: Have you tried writing code that doesn't throw and instead uses the type system and validations to handle the edge cases? If not, please try it sometime, at least on one of your side projects, I promise you'll not regret it ^_^

Thread Thread
joelbonetr profile image
JoelBonetR

I'll do it for sure, thank you for your comments, much appreciated! I'll start a new project in september so as I'm the one choosing the stack and designing the implementation thingy I may take a try 😁

Btw I've sent you a connection request through LinkedIn (unless someone else stole your name πŸ˜…).

Collapse
htho profile image
Hauke T.

Nice collection.

Please carefully edit your code snippets:

  • there are asterisks (*) prefixed with a backslash (\) and there are underscores (_) where there should be asterisks.
  • some array functions have a second generic type named underscore (_) some of these underscores are prefixed with a backslash (\)
  • there is a list of Month names, where "November" has leading whitespace

If I read these code-snippets and see those "mistakes" I don't know if I should trust these.

Collapse
chrisciokler profile image
Christian Prado Ciokler Author

Thank you for spotting those errors. I'll fix them right away.
Some kind of error with the markdown at the time I pass it.

Collapse
htho profile image
Hauke T.

Thanks.

Whats up with those underscore generics I can't see their purpose?

Collapse
moopet profile image
Ben Sinclair

A lot of these are pretty dangerous.

Not because they're wrong, necessarily, but more because they're "one-liners" in the same way that people's "pure CSS" drawings are done in pure CSS.

When you get to the point of having a "one-liner" that's 348 characters long, you have to consider getting a wider monitor. Like, a 65" wide monitor. Otherwise, you're going to wrap. And you know where you're going to wrap? At the points where you would reasonably be expected to have had a line break.

You can take any application and minimise it to one line if you want, you can make the variable names terse and impenetrable, you can exclude guard clauses and make assumptions about things other than just types, but it doesn't make good code.

There's nothing wrong with these ideas, but why force them onto one long, long, unreadably-long line for the sake of a catchy post title?

"Short, re-usable Typescript snippets" would describe them just as well then.

Collapse
retr0ville profile image
Retroville

Do these exist in a Git Repo you can link?

Collapse
chrisciokler profile image
Christian Prado Ciokler Author

Is a work in progress but you should find all the liners.

github.com/chrisciokler/typescript...

Collapse
retr0ville profile image
Retroville

This is great, thank you.

🌚 Browsing with dark mode makes you a better developer.

It's a scientific fact.