const numberArray =
['-10', '0', '10', '20', '30', 'NaN', 'NaN100'];
const parsedInts =
numberArray.map(parseInt);
import { toReadableNumber } from 'some-library';
const readableNumbers = someNumbers.map(toReadableNumber);
// toReadableNumber(10000000)
// → '10,000,000'
// We think of:
const readableNumbers = someNumbers.map(toReadableNumber);
// …as being like:
const readableNumbers = someNumbers.map((n) => toReadableNumber(n));
// …but it's more like:
const readableNumbers = someNumbers.map((item, index, arr) =>
toReadableNumber(item, index, arr),
);
// Not works
export function toReadableNumber(num, base = 10) {
// ...
}
// A promise for the next frame:
const nextFrame =
() => new Promise(requestAnimationFrame);
// A promise for the next frame:
const nextFrame =
() => new Promise(requestAnimationFrame);
// It is actually ...
const nextFrame = () =>
new Promise(
(resolve, reject) => requestAnimationFrame(resolve, reject)
);
const numberArray =
['-10', '0', '10', '20', '30', 'NaN', 'NaN100'];
const parsedInts =
numberArray.map(parseInt);
// Result
// → [-10, NaN, 2, 6, 12, NaN, NaN]
const controller = new AbortController();
const { signal } = controller;
el.addEventListener('mousemove', callback, { signal });
el.addEventListener('pointermove', callback, { signal });
el.addEventListener('touchmove', callback, { signal });
// Later, remove all three listeners:
controller.abort();
// Oops
const controller = new AbortController();
el.addEventListener(name, callback, controller);
// Ok
const controller = new AbortController();
const options = { signal: controller.signal };
el.addEventListener(name, callback, options);
function oneArg(arg1: string) {
console.log(arg1);
}
oneArg('hello', 'world');
// ^^^^^^^
// Expected 1 arguments, but got 2.
function twoArgCallback(cb: (arg1: string, arg2: string) => void) {
cb('hello', 'world');
}
twoArgCallback(oneArg);
// Oops
function toReadableNumber(num: number, whatever: string): string {
// Return num as string in a human readable form.
// Eg 10000000 might become '10,000,000'
return '';
}
const readableNumbers = [1, 2, 3].map(toReadableNumber);
// Ok
function toReadableNumber(num: number, whatever: number): string {
// Return num as string in a human readable form.
// Eg 10000000 might become '10,000,000'
return '';
}
const readableNumbers = [1, 2, 3].map(toReadableNumber);
interface Options {
reverse?: boolean;
}
function whatever({ reverse = false }: Options = {}) {
console.log(reverse);
}
whatever({ reverse: true });
What about 'toString', 'constructor', 'valueOf', 'hasOwnProperty' ...
const numbers = [1, 2, 3];
const doubledNumbers = numbers.map(
(n) => n * 2
);
What about other Web APIs ...
$.style(element, "top", rect.top);
$.style(element, "top", rect.top);
// 1
$.style($$(".popup"), "top", rect.top);
// 2
$.style(element, {
top: rect.top,
right: rect.right,
bottom: rect.bottom,
left: rect.left
);
// 3
$.style($$(".popup"), {
top: rect.top,
right: rect.right,
bottom: rect.bottom,
left: rect.left
});
$.style([element], {top: rect.top});
let values = {
top: rect.top,
right: rect.right,
bottom: rect.bottom,
left: rect.left
};
for (let element of $$(".popup")) {
for (let property in values) {
$.style(element, property, values[property]);
}
}
for (let element of $$(".popup")) {
Object.assign(element.style, {
top: rect.top,
right: rect.right,
bottom: rect.bottom,
left: rect.left
});
}
function style(subject, ...args) {
if (Array.isArray(subject)) {
subject.forEach(e => style(e, ...args));
}
else if ($.type(args[0]) === "object" && args.length = 1) {
for (let p in args[0]) {
style(subject, p, args[0][p]);
}
}
else {
subject.style[args[0]] = args[1];
}
return subject;
}
In JS, overloading is typically implemented by inspecting the types and number of a function’s arguments in the function, and branching accordingly.
export default function style(subject, ...args) {
return overload(
subject,
args,
(element, property, value) => {
element.style[property] = value;
}
)
}
export default overload(
function style(element, property, value) {
element.style[property] = value;
}
);
function style(element, property, value) {
element.style[property] = value;
}
export default overload(style);
enum AlertColor {
Red = "red",
Yellow = "yellow",
Green = "green",
}
const getColorForStockAmount = (stock = 0): AlertColor => {
if (stock <= 100) {
return AlertColor.Red;
}
if (stock <= 500) {
return AlertColor.Yellow;
}
return AlertColor.Green;
};
getColorForStockAmount(50); // returns red
getColorForStockAmount(250); // returns yellow
getColorForStockAmount(750); // returns green
if (stock <= 100) {
return AlertColor.Red;
}
+ if (stock <= 300) { // Add line
+ return AlertColor.Orange; // Add line
+ } // Add line
if (stock <= 500) {
return AlertColor.Yellow;
}
enum AlertColor {
Red = "red",
Yellow = "yellow",
Green = "green",
}
type Amount = {
amount: number;
color: AlertColor;
};
const getColorForStockAmount = (stock = 0): AlertColor => {
const items: Amount[] = [
{ amount: 100, color: AlertColor.Red },
{ amount: 500, color: AlertColor.Yellow },
{ amount: Number.MAX_SAFE_INTEGER, color: AlertColor.Green },
];
// .find() will return the first element that returns true for stock <= amount
const item = items.find((item) => stock <= item.amount);
return item?.color;
};
Change the function parameter from a number to an object with options
/** @deprecated
* Use { amount: number; updatedAt?: string; } object instead
* */
const getColorForStockAmount = (stock = 0): AlertColor => {
// ...
}
const getColorFromOptionsForStockAmount = (options: Options) => {
// ...
}
const getColorForStockAmount: StockOptions = (options: number | Options) => {
const { amount, updatedAt } =
typeof options === "number" ? { amount: options, updatedAt: undefined } : options;
const items: Amount[] = [
{ amount: 100, color: AlertColor.Red, updatedAt: new Date("2021-01-21") },
{ amount: 500, color: AlertColor.Yellow, updatedAt: new Date("2021-01-21") },
{ amount: 750, color: AlertColor.Green, updatedAt: new Date("2021-01-21") },
];
const item = items.find((item) => {
if (updatedAt) {
return amount <= item.amount && updatedAt < item.updatedAt;
}
return amount <= item.amount;
});
return item?.color;
};
const getColorForStockAmount: StockOptions = (options: number | Options) => {
const { amount, updatedAt } =
typeof options === "number" ? { amount: options, updatedAt: undefined } : options;
const items: Amount[] = [
{ amount: 100, color: AlertColor.Red, updatedAt: new Date("2021-01-21") },
{ amount: 500, color: AlertColor.Yellow, updatedAt: new Date("2021-01-21") },
{ amount: 750, color: AlertColor.Green, updatedAt: new Date("2021-01-21") },
];
const item = items.find((item) => {
if (updatedAt) {
return amount <= item.amount && updatedAt < item.updatedAt;
}
return amount <= item.amount;
});
return item?.color;
};
const getColorForStockAmount: StockOptions = (options: number | Options) => {
const { amount, updatedAt } =
typeof options === "number" ? { amount: options, updatedAt: undefined } : options;
const items: Amount[] = [
{ amount: 100, color: AlertColor.Red, updatedAt: new Date("2021-01-21") },
{ amount: 500, color: AlertColor.Yellow, updatedAt: new Date("2021-01-21") },
{ amount: 750, color: AlertColor.Green, updatedAt: new Date("2021-01-21") },
];
const item = items.find((item) => {
if (updatedAt) {
return amount <= item.amount && updatedAt < item.updatedAt;
}
return amount <= item.amount;
});
return item?.color;
};
// Define the interface for the function type with a list of overloads
interface StockOptions {
// The new signature
(options: Options): AlertColor | undefined;
/** @deprecated
* Use { amount: number; updatedAt?: string; } object instead
* */
(stock: number): AlertColor | undefined;
}
function evaluatesToFive(num, fn) {
return fn(num) === 5;
}
function divideByTwo(num) {
return num / 2;
}
evaluatesToFive(10, divideByTwo);
// true
evaluatesToFive(20, divideByTwo);
// false
function multiplyBy(num1) {
return function(num2) {
return num1 * num2;
};
}
const multiplyByThree = multiplyBy(3);
const multiplyByFive = multiplyBy(5);
multipyByThree(10); // 30
multiplyByFive(10); // 50
const newUser = {
age: 24,
password: 'some long password',
agreeToTerms: true,
};
function oldEnough(user) {
return user.age >= 18;
}
function passwordLongEnough(user) {
return user.password.length >= 8;
}
function agreeToTerms(user) {
return user.agreeToTerms === true;
}
function validate(obj, ...tests) {
for (let i = 0; i < tests.length; i++) {
if (tests[i](obj) === false) {
return false;
}
}
return true;
}
const newUser1 = {
age: 40,
password: 'tncy4ty49r2mrx',
agreeToTerms: true,
};
validate(newUser1, oldEnough, passwordLongEnough, agreeToTerms);
// true
const newUser2 = {
age: 40,
password: 'short',
agreeToTerms: true,
};
validate(newUser2, oldEnough, passwordLongEnough, agreeToTerms);
// false
function createValidator(...tests) {
return function(obj) {
for (let i = 0; i < tests.length; i++) {
if (tests[i](obj) === false) {
return false;
}
}
return true;
};
}
const userValidator = createValidator(
oldEnough,
passwordLongEnough,
agreeToTerms
);
userValidator(newUser1); // true
userValidator(newUser2); // false