Other Notes
JavaScript for pythonistas
Python | JavaScript | |
---|---|---|
Code Editor / IDE | PyCharm, VS Code | Atom, VS Code, WebStorm |
Code Formatter | black | Prettier |
Dependency Manager | Pipenv, poetry | bower (deprecated), npm, yarn |
Documentation Tool | Sphinx | JSDoc, sphinx-js |
Interpreter | bpython, ipython, python | node |
Library | requests, dateutil | axios, moment |
Linter | flake8, pyflakes, pylint | eslint, tslint |
Package Manager | pip, twine | bower (deprecated), npm, yarn |
Package Registry | PyPI | npm |
Package Runner | pipx | npx |
Runtime Manager | pyenv | nvm |
Scaffolding Tool | cookiecutter | cookiecutter, Yeoman |
Test Framework | doctest, nose, pytest | Jasmine, Jest, Mocha |
Web Framework | Django, Flask, Tornado | Angular, React, Vue.js |
Lama Dev - Simple JS projects
Github: 0xarthurxyz/ vanilla-js-css-html
Source: Youtube - 3 Javascript Projects Every Beginner Should Build
display: flex
In the flex layout model, the children of a flex container can be laid out in any direction, and can “flex” their sizes, either growing to fill unused space or shrinking to avoid overflowing the parent. Both horizontal and vertical alignment of the children can be easily manipulated.
Source: MDN docs
Dynamic table libraries
- React-data-grid (very impressive)
- Rsuite-table (very impressive)
- react-table
- Material-ui-table
- React-bootstrap-table
- ka-table
- tanStack table
Source: copycat.dev - react table
react-data-grid
(npm package)
Demo: https://adazzle.github.io/react-data-grid/#/common-features
Code examples:
- All features: https://github.com/adazzle/react-data-grid/blob/main/website/demos/AllFeatures.tsx
- Common features: https://github.com/adazzle/react-data-grid/blob/main/website/demos/CommonFeatures.tsx
CSS Dot Notation Naming Convention
Source: Stack Overflow
A dot in css is for what is called a class.
They can be called almost anything, for example in your CSS you would create a class and add style for it (in this case, I’m making the background black);
.my-first-class {
background-color: #000;
...
}
and to apply this class to an HTML element, you would do the following
<body class="my-first-class">
...
</body>
this would mean the body of the page would become black.
Now, you can create classes for CSS style or you can reference HTML elements directly, for example (CSS again);
body {
background-color: #000;
}
would directly reference the <body>
element on the page and do the same again.
The main difference between the two is that CSS classes are reusable. By comparison, referencing the HTML tag directly will affect all tags on the page (that are the same), for example (CSS again);
body {
background-color: #000;
}
.my-first-class {
background-color: #FFF;
}
and now for some HTML;
<body>
<p class="my-first-class">This is the first line</p>
<p class="my-first-class">This is the second line</p>
</body>x
Learning Functional Programming with JavaScript - JSUnconf
Source: Learning Functional Programming with JavaScript - Anjana Vakil - JSUnconf
Slides:
Avoid imperative style (for, while, if, else, etc.), use functions instead (input -> output)
Not functional:
var name = "Anjana";
var greeting = "Hi, I’m ";
console.log(greeting + name);
// => “Hi, I’m Anjana”
Functional:
function greet(name) {
return "Hi, I’m ” + name";
}
greet("Anjana");
// => “Hi, I’m Anjana”
Avoid side effects, use pure functions.
Not pure:
var name = "Anjana";
function greet() {
console.log("Hi, I’m " + name);
}
Pure:
function greet(name) {
return "Hi, I’m " + name;
}
Use higher-order functions (functions that take functions as arguments or return functions)
function makeAdjectifier(adjective) {
return function (string) {
return adjective + “ ” + string;
};
}
var coolifier = makeAdjectifier(“cool”);
coolifier(“conference”);
// => “cool conference”
Avoid loops, use map, filter, reduce
Avoid mutability (changing objects in place), use immutable data
Mutable:
var rooms = [“H1”, “H2”, “H3”];
rooms[2] = “H4”;
rooms;
// => ["H1", "H2", "H4"]
Immutable:
var rooms = [“H1”, “H2”, “H3”];
var newRooms = rooms.map(function (rm) {
if (rm === “H3”) {
return “H4”;
} else {
return rm;
}
});
newRooms;
// => ["H1", "H2", "H4"]
rooms;
// => ["H1", "H2", "H3"]
Consider using persistent data structures for efficient immutability to avoid copying immutable data all the time.
For example, ideal hash trees use nodes to share data between versions. Ideal Hash Trees were invented by Phil Bagwell around 2001.
Some libraries for functional programming in JS:
What the heck is the event loop anyway? | Philip Roberts | JSConf EU
Source: What the heck is the event loop anyway?
- Transcript: What the heck is the event loop anyway?
- LinkedIn: Philip Roberts
(Reminder) You always put things on top of the stack and pop them of the top of the stack.
With JS, function calls are added to the stack and removed from the stack after they return
.
Callbacks sitting in the callback queue only get back on the call stack when the call stack is empty.
JS runtime isn’t only the engine (V8), it also has a heap (for memory), a call stack, external WebAPIs you can call (or Node APIs in C++ for backend apps), a callback queue, and an event loop.
setTimeout
is not guaranteed to run after the specified time, it is guaranteed to run at least
after the time specified if the call stack is empty (“it’s a minimum delay not maximum delay”).
Example with setTimeout(..., 0)
that only executes when the call stack is empty again and not
after 0ms (immediately).
Franziska Hinkelmann: JavaScript engines - how do they even? | JSConf EU
- Article: Medium - Understanding V8’s Bytecode
- Slides (
.key
): Presentations/JSConfEU2017.key - Slides (
.pdf
): JavaScript Engines - how do they even? - Video: JavaScript engines - how do they even? | JSConf EU
JS is just in time compiled (JIT) which means the code is not first compiled and then run, but compiled step by step as the execution happens.
Because JS is dynamically typed, the compiler can’t know the types of variables ahead of time, which is typically required to optimise the code for execution. So instead, JS engines optimise code that is run often (“hot code”) and deoptimise code that is run rarely.
Essentially JS code is (1) parsed, (2) turned into an abstract syntax tree (AST) , (3) compiled into (“average”) assembly machine code (baseline compiler), and (4) optimised into more performant machine code (optimising compiler) when the code is run often.
Because of this, changing types often makes it harder for the compiler to optimise code. So the “optimizing compiler uses previously seen type information - don’t change types!”. To get the best performance out code, consider writing code that looks like statically typed.
Computed names in object literal definitions
-
ES5
function foo() { let o = {}; o[x] = 1; return o; }
-
ES6
function foo() { return {[x]: 1}; }
Inspect compiler code with Node.js or Chrome:
—print-opt-code
: code generated by optimizing compiler—print-bytecode
: bytecode generated by interpreter—trace-ic
: different object types a call sites encounters—trace-opt
and—trace-deopt
: which functions are (de)optimized
Adopting Typescript at Scale - Brie Bunge | JSConf Hawaii 2019
- YouTube: Adopting Typescript at Scale
Parameter types:
function greet(name) {
return `Hello, ${name}!`;
}
function greet(name: string) {
return `Hello, ${name}!`;
}
greeter('JSConf Hawaii'); // compiles fine
greeter(['JSConf', 'Hawaii']); // compile error
Types of other objects:
interface Person {
firstName: string;
lastName: string;
}
function greet(person: Person) {
return `Hello, ${person.firstName} ${person.lastName}!`;
}
Great editor integrations with autocompletion and typechecking.
At Airbnb they have 2m+ lines of JS code and 100+ internal npm packages. 1300+ engineers of which 200+ are frontend engineers.
Reasons they were interested in TS:
- Fewer bugs (which stand in the way of helping users)
- Better developer experience
- end-to-end type safety (from backend API to frontend app)
Type script declaration files (.d.ts
) are used to describe the shape of JS code.
This helped Airbnb circumvent the circular dependency problem between JS and TS code,
e.g. the repo depending on npm packages that are written in JS (i.e. without type safety).
// .d.ts file
export default function greeter(name: string): string;
// .js file
export default function greeter(name) {
return `Hello, ${name}!`;
}
Declaration files are handy because they can be shared across multiple repos.
This is how types for React (@types/react
) and others (@types/*
) come about.
They are maintained by a community on Github
DefinitelyTyped/DefinitelyTyped
.
This works well for public npm packages, for internal npm packages they mirrored the
DefinitelyTyped pattern and saved all internal types at @airbnb-types/*
(see GitHub repo:
brieb/types-starter
).
Brie read tons of postmortems to see if JavaScript related problems could have been prevented with TypeScript. Examples:
- Missing parameters in function calls can be prevented with “Expected 1 argument, but got 0”
- Strict null-checking
- “Object is possibly ’null’ or ‘undefined’”
- “Cannot invoke an object which is possibly ’null’ or ‘undefined’”
- Type mismatches “Type ‘…’ is not assignable to type ‘…’”
Brie found that 38% of Airbnb bugs were preventable with TypeScript according to postmortems.
ASTExplorer.net for exploring ASTs of JS and TS code.
Some tips on large migrations:
- gather evidence and support
- gradually introduce change
- provide a migration path
Some things to learn
Must have:
- Basic Jest testing framework
- Basic JS programming patterns (e.g. closures, promises, async/await)
- TypeScript typing function parameters and return types
- Basic Express.js
- Basic MongoDB (mongoose)
Nice to have:
- New programming patterns (differences between ES5 vs ES6)
- Understand what event loop looks like and how it works (browser and node)
- Basic React
How I Learned To Stop Worrying And Trust The Compiler - Felix Rieseberg - Node Summit
tsc -init
to initialise a typescript project. This simply creates a tsconfig.json
file.
tsc -w
to watch for changes and compile TS to JS automatically (so you can see changes as you go).
Example used in the talk:
// demo.ts
interface Person {
name: string;
age: number;
}
class PersonManager {
hello: string;
constructor() {
this.hello = 'foo';
}
}
function sortPeople(input = Person[] = []) {
const result = input.slice(0);
result.sort((a, b) => {
return a.name.localeCompare(b.name);
});
return result;
}
sortPeople(5); // Error: Argument of type '5' is not assignable to parameter of type 'any []'
TypeScript has a very neat built-in definition feature (“peek definition”) for vanilla JS functions so you don’t have to look up the JS documentation in the browser.
Airbnb JavaScript Style Guide
Source:
Github airbnb/javascript
Below is a selection of stylistic rules to highlight.
References
-
2.1 Use
const
for all of your references; avoid usingvar
. eslint:prefer-const
,no-const-assign
Why? This ensures that you can’t reassign your references, which can lead to bugs and difficult to comprehend code.
// bad var a = 1; var b = 2; // good const a = 1; const b = 2;
-
2.2 If you must reassign references, use
let
instead ofvar
. eslint:no-var
Why?
let
is block-scoped rather than function-scoped likevar
.// bad var count = 1; if (true) { count += 1; } // good, use the let. let count = 1; if (true) { count += 1; }
-
2.3 Note that both
let
andconst
are block-scoped, whereasvar
is function-scoped.// const and let only exist in the blocks they are defined in. { let a = 1; const b = 1; var c = 1; } console.log(a); // ReferenceError console.log(b); // ReferenceError console.log(c); // Prints 1
In the above code, you can see that referencing
a
andb
will produce a ReferenceError, whilec
contains the number. This is becausea
andb
are block scoped, whilec
is scoped to the containing function.
Objects
-
3.1 Use the literal syntax for object creation. eslint:
no-new-object
// bad const item = new Object(); // good const item = {};
-
3.2 Use computed property names when creating objects with dynamic property names.
Why? They allow you to define all the properties of an object in one place.
function getKey(k) { return `a key named ${k}`; } // bad const obj = { id: 5, name: 'San Francisco', }; obj[getKey('enabled')] = true; // good const obj = { id: 5, name: 'San Francisco', [getKey('enabled')]: true, };
-
3.4 Use property value shorthand. eslint:
object-shorthand
Why? It is shorter and descriptive.
const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { lukeSkywalker: lukeSkywalker, }; // good const obj = { lukeSkywalker, };
-
3.5 Group your shorthand properties at the beginning of your object declaration.
Why? It’s easier to tell which properties are using the shorthand.
const anakinSkywalker = 'Anakin Skywalker'; const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { episodeOne: 1, twoJediWalkIntoACantina: 2, lukeSkywalker, episodeThree: 3, mayTheFourth: 4, anakinSkywalker, }; // good const obj = { lukeSkywalker, anakinSkywalker, episodeOne: 1, twoJediWalkIntoACantina: 2, episodeThree: 3, mayTheFourth: 4, };
-
3.6 Only quote properties that are invalid identifiers. eslint:
quote-props
Why? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines.
// bad const bad = { 'foo': 3, 'bar': 4, 'data-blah': 5, }; // good const good = { foo: 3, bar: 4, 'data-blah': 5, };
Arrays
-
4.1 Use the literal syntax for array creation. eslint:
no-array-constructor
// bad const items = new Array(); // good const items = [];
-
4.2 Use Array#push instead of direct assignment to add items to an array.
const someStack = []; // bad someStack[someStack.length] = 'abracadabra'; // good someStack.push('abracadabra');
-
4.3 Use array spreads
...
to copy arrays.// bad const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i += 1) { itemsCopy[i] = items[i]; } // good const itemsCopy = [...items];
-
4.7 Use return statements in array method callbacks. It’s ok to omit the return if the function body consists of a single statement returning an expression without side effects, following 8.2 . eslint:
array-callback-return
// good [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); // good [1, 2, 3].map((x) => x + 1); // bad - no returned value means `acc` becomes undefined after the first iteration [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { const flatten = acc.concat(item); }); // good [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { const flatten = acc.concat(item); return flatten; }); // bad inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; } else { return false; } }); // good inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; } return false; });
-
4.8 Use line breaks after opening array brackets and before closing array brackets, if an array has multiple lines
// bad const arr = [ [0, 1], [2, 3], [4, 5], ]; const objectInArray = [{ id: 1, }, { id: 2, }]; const numberInArray = [ 1, 2, ]; // good const arr = [[0, 1], [2, 3], [4, 5]]; const objectInArray = [ { id: 1, }, { id: 2, }, ]; const numberInArray = [ 1, 2, ];
Strings
-
6.1 Use single quotes
''
for strings. eslint:quotes
// bad const name = "Capt. Janeway"; // bad - template literals should contain interpolation or newlines const name = `Capt. Janeway`; // good const name = 'Capt. Janeway';
-
6.3 When programmatically building up strings, use template strings instead of concatenation. eslint:
prefer-template
template-curly-spacing
Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.
// bad function sayHi(name) { return 'How are you, ' + name + '?'; } // bad function sayHi(name) { return ['How are you, ', name, '?'].join(); } // bad function sayHi(name) { return `How are you, ${ name }?`; } // good function sayHi(name) { return `How are you, ${name}?`; }
Functions
-
7.1 Use named function expressions instead of function declarations. eslint:
func-style
,func-names
Why? Function declarations are hoisted, which means that it’s easy - too easy - to reference the function before it is defined in the file. This harms readability and maintainability. If you find that a function’s definition is large or complex enough that it is interfering with understanding the rest of the file, then perhaps it’s time to extract it to its own module! Don’t forget to explicitly name the expression, regardless of whether or not the name is inferred from the containing variable (which is often the case in modern browsers or when using compilers such as Babel). This eliminates any assumptions made about the Error’s call stack. ( Discussion )
// bad function foo() { // ... } // bad const foo = function () { // ... }; // good // lexical name distinguished from the variable-referenced invocation(s) const short = function longUniqueMoreDescriptiveLexicalFoo() { // ... };
-
7.7 Use default parameter syntax rather than mutating function arguments.
// really bad function handleThings(opts) { // No! We shouldn’t mutate function arguments. // Double bad: if opts is falsy it'll be set to an object which may // be what you want but it can introduce subtle bugs. opts = opts || {}; // ... } // still bad function handleThings(opts) { if (opts === void 0) { opts = {}; } // ... } // good function handleThings(opts = {}) { // ... }
-
7.8 Avoid side effects with default parameters.
Why? They are confusing to reason about.
let b = 1; // bad function count(a = b++) { console.log(a); } count(); // 1 count(); // 2 count(3); // 3 count(); // 3
-
7.9 Always put default parameters last. eslint:
default-param-last
// bad function handleThings(opts = {}, name) { // ... } // good function handleThings(name, opts = {}) { // ... }
-
7.11 Spacing in a function signature. eslint:
space-before-function-paren
space-before-blocks
Why? Consistency is good, and you shouldn’t have to add or remove a space when adding or removing a name.
// bad const f = function(){}; const g = function (){}; const h = function() {}; // good const x = function () {}; const y = function a() {};
-
7.12 Never mutate parameters. eslint:
no-param-reassign
Why? Manipulating objects passed in as parameters can cause unwanted variable side effects in the original caller.
// bad function f1(obj) { obj.key = 1; } // good function f2(obj) { const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; }
-
7.15 Functions with multiline signatures, or invocations, should be indented just like every other multiline list in this guide: with each item on a line by itself, with a trailing comma on the last item. eslint:
function-paren-newline
// bad function foo(bar, baz, quux) { // ... } // good function foo( bar, baz, quux, ) { // ... } // bad console.log(foo, bar, baz); // good console.log( foo, bar, baz, );
Arrow Functions
-
8.1 When you must use an anonymous function (as when passing an inline callback), use arrow function notation. eslint:
prefer-arrow-callback
,arrow-spacing
Why? It creates a version of the function that executes in the context of
this
, which is usually what you want, and is a more concise syntax.Why not? If you have a fairly complicated function, you might move that logic out into its own named function expression.
// bad [1, 2, 3].map(function (x) { const y = x + 1; return x * y; }); // good [1, 2, 3].map((x) => { const y = x + 1; return x * y; });
-
8.4 Always include parentheses around arguments for clarity and consistency. eslint:
arrow-parens
Why? Minimizes diff churn when adding or removing arguments.
// bad [1, 2, 3].map(x => x * x); // good [1, 2, 3].map((x) => x * x); // bad [1, 2, 3].map(number => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); // good [1, 2, 3].map((number) => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); // bad [1, 2, 3].map(x => { const y = x + 1; return x * y; }); // good [1, 2, 3].map((x) => { const y = x + 1; return x * y; });
Classes & Constructors
-
9.1 Always use
class
. Avoid manipulatingprototype
directly.Why?
class
syntax is more concise and easier to reason about.// bad function Queue(contents = []) { this.queue = [...contents]; } Queue.prototype.pop = function () { const value = this.queue[0]; this.queue.splice(0, 1); return value; }; // good class Queue { constructor(contents = []) { this.queue = [...contents]; } pop() { const value = this.queue[0]; this.queue.splice(0, 1); return value; } }
-
9.2 Use
extends
for inheritance.Why? It is a built-in way to inherit prototype functionality without breaking
instanceof
.// bad const inherits = require('inherits'); function PeekableQueue(contents) { Queue.apply(this, contents); } inherits(PeekableQueue, Queue); PeekableQueue.prototype.peek = function () { return this.queue[0]; }; // good class PeekableQueue extends Queue { peek() { return this.queue[0]; } }
-
9.5 Classes have a default constructor if one is not specified. An empty constructor function or one that just delegates to a parent class is unnecessary. eslint:
no-useless-constructor
// bad class Jedi { constructor() {} getName() { return this.name; } } // bad class Rey extends Jedi { constructor(...args) { super(...args); } } // good class Rey extends Jedi { constructor(...args) { super(...args); this.name = 'Rey'; } }
-
9.7 Class methods should use
this
or be made into a static method unless an external library or framework requires using specific non-static methods. Being an instance method should indicate that it behaves differently based on properties of the receiver. eslint:class-methods-use-this
// bad class Foo { bar() { console.log('bar'); } } // good - this is used class Foo { bar() { console.log(this.bar); } } // good - constructor is exempt class Foo { constructor() { // ... } } // good - static methods aren't expected to use this class Foo { static bar() { console.log('bar'); } }
Modules
-
10.1 Always use modules (
import
/export
) over a non-standard module system. You can always transpile to your preferred module system.Why? Modules are the future, let’s start using the future now.
// bad const AirbnbStyleGuide = require('./AirbnbStyleGuide'); module.exports = AirbnbStyleGuide.es6; // ok import AirbnbStyleGuide from './AirbnbStyleGuide'; export default AirbnbStyleGuide.es6; // best import { es6 } from './AirbnbStyleGuide'; export default es6;
-
10.2 Do not use wildcard imports.
Why? This makes sure you have a single default export.
// bad import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // good import AirbnbStyleGuide from './AirbnbStyleGuide';
-
10.3 And do not export directly from an import.
Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent.
// bad // filename es6.js export { es6 as default } from './AirbnbStyleGuide'; // good // filename es6.js import { es6 } from './AirbnbStyleGuide'; export default es6;
-
10.4 Only import from a path in one place. eslint:
no-duplicate-imports
Why? Having multiple lines that import from the same path can make code harder to maintain.
// bad import foo from 'foo'; // … some other imports … // import { named1, named2 } from 'foo'; // good import foo, { named1, named2 } from 'foo'; // good import foo, { named1, named2, } from 'foo';
-
10.8 Multiline imports should be indented just like multiline array and object literals. eslint:
object-curly-newline
Why? The curly braces follow the same indentation rules as every other curly brace block in the style guide, as do the trailing commas.
// bad import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; // good import { longNameA, longNameB, longNameC, longNameD, longNameE, } from 'path';
-
10.10 Do not include JavaScript filename extensions eslint:
import/extensions
Why? Including extensions inhibits refactoring, and inappropriately hardcodes implementation details of the module you’re importing in every consumer.
// bad import foo from './foo.js'; import bar from './bar.jsx'; import baz from './baz/index.jsx'; // good import foo from './foo'; import bar from './bar'; import baz from './baz';
Iterators
-
11.1 Don’t use iterators. Prefer JavaScript’s higher-order functions instead of loops like
for-in
orfor-of
. eslint:no-iterator
no-restricted-syntax
Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side effects.
Use
map()
/every()
/filter()
/find()
/findIndex()
/reduce()
/some()
/ … to iterate over arrays, andObject.keys()
/Object.values()
/Object.entries()
to produce arrays so you can iterate over objects.const numbers = [1, 2, 3, 4, 5]; // bad let sum = 0; for (let num of numbers) { sum += num; } sum === 15; // good let sum = 0; numbers.forEach((num) => { sum += num; }); sum === 15; // best (use the functional force) const sum = numbers.reduce((total, num) => total + num, 0); sum === 15; // bad const increasedByOne = []; for (let i = 0; i < numbers.length; i++) { increasedByOne.push(numbers[i] + 1); } // good const increasedByOne = []; numbers.forEach((num) => { increasedByOne.push(num + 1); }); // best (keeping it functional) const increasedByOne = numbers.map((num) => num + 1);
Properties
-
12.1 Use dot notation when accessing properties. eslint:
dot-notation
const luke = { jedi: true, age: 28, }; // bad const isJedi = luke['jedi']; // good const isJedi = luke.jedi;
-
12.2 Use bracket notation
[]
when accessing properties with a variable.const luke = { jedi: true, age: 28, }; function getProp(prop) { return luke[prop]; } const isJedi = getProp('jedi');
Variables
-
13.1 Always use
const
orlet
to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. eslint:no-undef
prefer-const
// bad superPower = new SuperPower(); // good const superPower = new SuperPower();
-
13.2 Use one
const
orlet
declaration per variable or assignment. eslint:one-var
Why? It’s easier to add new variable declarations this way, and you never have to worry about swapping out a
;
for a,
or introducing punctuation-only diffs. You can also step through each declaration with the debugger, instead of jumping through all of them at once.// bad const items = getItems(), goSportsTeam = true, dragonball = 'z'; // bad // (compare to above, and try to spot the mistake) const items = getItems(), goSportsTeam = true; dragonball = 'z'; // good const items = getItems(); const goSportsTeam = true; const dragonball = 'z';
-
13.3 Group all your
const
s and then group all yourlet
s.Why? This is helpful when later on you might need to assign a variable depending on one of the previously assigned variables.
// bad let i, len, dragonball, items = getItems(), goSportsTeam = true; // bad let i; const items = getItems(); let dragonball; const goSportsTeam = true; let len; // good const goSportsTeam = true; const items = getItems(); let dragonball; let i; let length;
-
13.4 Assign variables where you need them, but place them in a reasonable place.
Why?
let
andconst
are block scoped and not function scoped.// bad - unnecessary function call function checkName(hasName) { const name = getName(); if (hasName === 'test') { return false; } if (name === 'test') { this.setName(''); return false; } return name; } // good function checkName(hasName) { if (hasName === 'test') { return false; } const name = getName(); if (name === 'test') { this.setName(''); return false; } return name; }
-
13.5 Don’t chain variable assignments. eslint:
no-multi-assign
Why? Chaining variable assignments creates implicit global variables.
// bad (function example() { // JavaScript interprets this as // let a = ( b = ( c = 1 ) ); // The let keyword only applies to variable a; variables b and c become // global variables. let a = b = c = 1; }()); console.log(a); // throws ReferenceError console.log(b); // 1 console.log(c); // 1 // good (function example() { let a = 1; let b = a; let c = a; }()); console.log(a); // throws ReferenceError console.log(b); // throws ReferenceError console.log(c); // throws ReferenceError // the same applies for `const`
-
13.6 Avoid using unary increments and decrements (
++
,--
). eslintno-plusplus
Why? Per the eslint documentation, unary increment and decrement statements are subject to automatic semicolon insertion and can cause silent errors with incrementing or decrementing values within an application. It is also more expressive to mutate your values with statements like
num += 1
instead ofnum++
ornum ++
. Disallowing unary increment and decrement statements also prevents you from pre-incrementing/pre-decrementing values unintentionally which can also cause unexpected behavior in your programs.// bad const array = [1, 2, 3]; let num = 1; num++; --num; let sum = 0; let truthyCount = 0; for (let i = 0; i < array.length; i++) { let value = array[i]; sum += value; if (value) { truthyCount++; } } // good const array = [1, 2, 3]; let num = 1; num += 1; num -= 1; const sum = array.reduce((a, b) => a + b, 0); const truthyCount = array.filter(Boolean).length;
-
13.7 Avoid linebreaks before or after
=
in an assignment. If your assignment violatesmax-len
, surround the value in parens. eslintoperator-linebreak
.Why? Linebreaks surrounding
=
can obfuscate the value of an assignment.// bad const foo = superLongLongLongLongLongLongLongLongFunctionName(); // bad const foo = 'superLongLongLongLongLongLongLongLongString'; // good const foo = ( superLongLongLongLongLongLongLongLongFunctionName() ); // good const foo = 'superLongLongLongLongLongLongLongLongString';
Testing: 30.2
Whenever you fix a bug, write a regression test. A bug fixed without a regression test is almost certainly going to break again in the future.
TypeScript for Java/C# Programmers
Source: typescriptlang.org
TLDR:
-
In Java:
-
everything belongs to a class or interface
-
it’s meaningful to think of a one-to-one correspondence between runtime types and their compile-time declarations
-
types are related to their declarations, not their structures.
-
the type system is (reified) nominal.
A type system is nominal, nominative, or name-based if compatibility and equivalence of data types is determined by explicit declarations and/or the name of the types. Nominal systems are used to determine if types are equivalent, as well as if a type is a subtype of another.
Nominal type systems contrast with structural systems , where comparisons are based on the structure of the types in question and do not require explicit declarations.
Source: wikipedia.org
-
-
In TypeScript:
- free functions (those not associated with a class) working over data without an implied OOP hierarchy are the preferred model for writing programs (in JavaScript more broadly)
- types are sets (a particular value can belong to many sets or types at the same time)
- classes and many common patterns such as interfaces, inheritance, and static methods are supported
We recommend learning a little bit of JavaScript without types first to understand JavaScript’s runtime behaviors. […] TypeScript uses the same runtime as JavaScript, so any resources about how to accomplish specific runtime behavior (converting a string to a number, displaying an alert, writing a file to disk, etc.) will always apply equally well to TypeScript programs.
Rethinking the Class
C# and Java are what we might call mandatory OOP languages. In these languages, the class is the basic unit of code organization, and also the basic container of all data and behavior at runtime. Forcing all functionality and data to be held in classes can be a good domain model for some problems, but not every domain needs to be represented this way.
Free Functions and Data
In JavaScript, functions can live anywhere, and data can be passed around freely without being inside a pre-defined
class
orstruct
. This flexibility is extremely powerful. “Free” functions (those not associated with a class) working over data without an implied OOP hierarchy tends to be the preferred model for writing programs in JavaScript.
Static Classes
Additionally, certain constructs from C# and Java such as singletons and static classes are unnecessary in TypeScript.
OOP in TypeScript
That said, you can still use classes if you like! Some problems are well-suited to being solved by a traditional OOP hierarchy, and TypeScript’s support for JavaScript classes will make these models even more powerful. TypeScript supports many common patterns such as implementing interfaces, inheritance, and static methods.
We’ll cover classes later in this guide.
Rethinking Types
TypeScript’s understanding of a type is actually quite different from C# or Java’s. Let’s explore some differences.
Nominal Reified Type Systems
In C# or Java, any given value or object has one exact type - either
null
, a primitive, or a known class type. We can call methods likevalue.GetType()
orvalue.getClass()
to query the exact type at runtime. The definition of this type will reside in a class somewhere with some name, and we can’t use two classes with similar shapes in lieu of each other unless there’s an explicit inheritance relationship or commonly-implemented interface.These aspects describe a reified, nominal type system. The types we wrote in the code are present at runtime, and the types are related via their declarations, not their structures.
Types as Sets
In C# or Java, it’s meaningful to think of a one-to-one correspondence between runtime types and their compile-time declarations.
In TypeScript, it’s better to think of a type as a set of values that share something in common. Because types are just sets, a particular value can belong to many sets at the same time.
Once you start thinking of types as sets, certain operations become very natural. For example, in C#, it’s awkward to pass around a value that is either a
string
orint
, because there isn’t a single type that represents this sort of value.In TypeScript, this becomes very natural once you realize that every type is just a set. How do you describe a value that either belongs in the
string
set or thenumber
set? It simply belongs to the union of those sets:string | number
.TypeScript provides a number of mechanisms to work with types in a set-theoretic way, and you’ll find them more intuitive if you think of types as sets.
Erased Structural Types
In TypeScript, objects are not of a single exact type. For example, if we construct an object that satisfies an interface, we can use that object where that interface is expected even though there was no declarative relationship between the two.
// Example
interface Pointlike {
x: number;
y: number;
}
interface Named {
name: string;
}
function logPoint(point: Pointlike) {
console.log("x = " + point.x + ", y = " + point.y);
}
function logName(x: Named) {
console.log("Hello, " + x.name);
}
const obj = {
x: 0,
y: 0,
name: "Origin",
};
logPoint(obj);
logName(obj);
Surpises with TS: Empty types
We can see that { k: 10 } has all of the properties that Empty does, because Empty has no properties. Therefore, this is a valid call
class Empty {}
function fn(arg: Empty) {
// do something?
}
// No error, but this isn't an 'Empty' ?
fn({ k: 10 });
How to Convert a Unix Timestamp to Time in JavaScript
Source: tutorialrepublic.com
Simply multiply Unix timestamp by 1000 to convert it to a JavaScript time, because Unix timestamp measures time as a number of seconds, whereas in JavaScript time is fundamentally specified as the number of milliseconds (elapsed since January 1, 1970 at 00:00:00 UTC).
// Timestamp in seconds
var unixTimestamp = 1651822834;
/* Create a new JavaScript Date object based on Unix timestamp.
Multiplied it by 1000 to convert it into milliseconds */
var date = new Date(unixTimestamp * 1000);
// Generate date string
console.log(date.toLocaleDateString("en-US")); // Prints: 5/6/2022
console.log(date.toLocaleDateString("en-GB")); // Prints: 06/05/2022
console.log(date.toLocaleDateString("default")); // Prints: 5/6/2022
// Generate time string
console.log(date.toLocaleTimeString("en-US")); // Prints: 1:10:34 PM
console.log(date.toLocaleTimeString("it-IT")); // Prints: 13:10:34
console.log(date.toLocaleTimeString("default")); // Prints: 1:10:34 PM
Mozilla - Date.prototype.toLocaleDateString()
Source: developer.mozilla.org
Syntax:
toLocaleDateString()
toLocaleDateString(locales)
toLocaleDateString(locales, options) // prints: "Thursday, December 20, 2012, UTC"
locale
:
// US English uses month-day-year order
console.log(date.toLocaleDateString("en-US"));
// "12/20/2012"
// British English uses day-month-year order
console.log(date.toLocaleDateString("en-GB"));
// "20/12/2012"
// Korean uses year-month-day order
console.log(date.toLocaleDateString("ko-KR"));
// "2012. 12. 20."
options
:
const options = {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
};
Example:
const date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
// request a weekday along with a long date
const options = {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
};
console.log(date.toLocaleDateString("de-DE", options));
// "Donnerstag, 20. Dezember 2012"
// an application may want to use UTC and make that visible
options.timeZone = "UTC";
options.timeZoneName = "short";
console.log(date.toLocaleDateString("en-US", options));
// "Thursday, December 20, 2012, UTC"
JSON Object v. JavaScript Object
Source: medium.com
TLDR:
-
JavaScript objects
- are key-value pairs that can contain strings, numbers, arrays, functions, booleans, and other objects
// JavaScript Object const jsObj = { name: 'Alice', age: 30, };
-
JSON objects
-
are text-only (that means both keys and values are
strings
)// JSON Object const jsonObj = { "name": "Alice", "age": "30", };
-
you can validate JSON objects using jsonlint.com
-
What is JSON?
JavaScript Object Notation. JSON syntax is derived from Javascript objects, but the JSON format is text only, meaning a JSON object is quite literally just a string.
When do we use a JSON object?
JSON is used to read data from a web server and then display that data onto a webpage. When we exchange data between a browser and a server it can only be text, hence we can use JSON for this.
We want to read a JSON object (essentially our string of data) and convert it into a JavaScript Object.
Methods to convert JSON:
-
Receiving Data
The best way to do this is to use this built-in JavaScript function
JSON.parse()
.JSON.parse()
will convert text into a JavaScript object. -
Sending Data
JSON.stringify()
will convert a JavaScript object into a JSON object that can then be sent to a web server.
What is a JavaScript Object?
A Javascript object has a similar syntax to JSON, it uses curly braces and key/value pairs. The main difference in syntax is that in a JSON object the keys must be a string written with double quotes. In JavaScript, a key can be
strings
,numbers
, or identifier names, and the strings can be written in single or double quotes.
// JavaScript Object
const jsObj = {
name: 'Alice',
age: 30,
};
// JSON Object
const jsonObj = {
"name": "Alice",
"age": "30",
};
You validate JSON objects using jsonlint.com .
The JavaScript language > Objects: the basics
Source: javascript.info
- Read more details about JavaScript objects.
Require vs. Import in JavaScript
Source: stackdiary.com
One of the main differences between require
and import
is that
require
can only be used to import modules, whereasimport
can be used to import both modules and individual exports from those modules.
In general, import
is preferred over require because it is a more modern and flexible syntax,
and it will eventually replace require
in the language.
For example, if you have a module named myModule
, you can use require
to import the
entire module like this:
const myModule = require('myModule');
To import a specific export from the module, you would need to use the .
notation like this:
const myFunction = require('myModule').myFunction;
Using import
, you can import the entire module and all of its exports like this:
import * as myModule from 'myModule';
Or you can import a specific export like this:
import {myFunction} from 'myModule';
The Difference Between NPM and Yarn
Source: dev.to
To see list of commands:
NPM - npm
Yarn - yarn
Install dependencies from package.json:
NPM - npm install
Yarn - yarn
Install a package and add to package.json:
NPM - npm install package --save
Yarn - yarn add package
Install a devDependency:
NPM - npm install package --save-dev
Yarn - yarn add package --dev
Remove a dependency:
NPM - npm uninstall package --save
Yarn - yarn remove package
Upgrade a package to its latest version:
NPM - npm update --save
Yarn - yarn upgrade
Install a package globally:
NPM - npm install package -g
Yarn - yarn global add package
TypeScript for JavaScript Programmers
Source: typescriptlang.org
Types by Inference
TypeScript knows the JavaScript language and will generate types for you in many cases. For example in creating a variable and assigning it to a particular value, TypeScript will use the value as its type.
let helloWorld = "Hello World";
// let helloWorld: string
By understanding how JavaScript works, TypeScript can build a type-system that accepts JavaScript code but has types. This offers a type-system without needing to add extra characters to make types explicit in your code.
You may have written JavaScript in Visual Studio Code, and had editor auto-completion. Visual Studio Code uses TypeScript under the hood to make it easier to work with JavaScript.
Defining Types
You can use a wide variety of design patterns in JavaScript. However, some design patterns make it difficult for types to be inferred automatically (for example, patterns that use dynamic programming). To cover these cases, TypeScript supports an extension of the JavaScript language, which offers places for you to tell TypeScript what the types should be.
For example, to create an object with an inferred type which includes
name: string
andid: number
, you can write:
const user = {
name: "Hayes",
id: 0,
};
You can explicitly describe this object’s shape using an
interface
declaration:
interface User {
name: string;
id: number;
}
You can then declare that a JavaScript object conforms to the shape of your new
interface
by using syntax like: TypeName
after a variable declaration:
const user: User = {
name: "Hayes",
id: 0,
};
If you provide an object that doesn’t match the interface you have provided, TypeScript will warn you:
interface User {
name: string;
id: number;
}
const user: User = {
username: "Hayes",
id: 0,
};
// Type '{ username: string; id: number; }' is not assignable to type 'User'.
// Object literal may only specify known properties, and 'username' does not exist in type 'User'.
Since JavaScript supports classes and object-oriented programming, so does TypeScript. You can use an interface declaration with classes:
interface User {
name: string;
id: number;
}
class UserAccount {
name: string;
id: number;
constructor(name: string, id: number) {
this.name = name;
this.id = id;
}
}
const user: User = new UserAccount("Murphy", 1);
You can use interfaces to annotate parameters and return values to functions:
function deleteUser(user: User) {
// ...
}
function getAdminUser(): User {
//...
}
There is already a small set of primitive types available in JavaScript:
boolean
,bigint
,null
,number
,string
,symbol
, andundefined
, which you can use in an interface. TypeScript extends this list with a few more, such asany
(allow anything),unknown
(ensure someone using this type declares what the type is),never
(it’s not possible that this type could happen), andvoid
(a function which returnsundefined
or has no return value).
You’ll see that there are two syntaxes for building types: Interfaces and Types . You should prefer
interface
. Usetype
when you need specific features.
Composing Types
With TypeScript, you can create complex types by combining simple ones. There are two popular ways to do so: with unions, and with generics.
Unions
With a union, you can declare that a type could be one of many types. For example, you can describe a
boolean
type > as being eithertrue
orfalse
:
`type MyBool = true | false;
Note: If you hover over
MyBool
above, you’ll see that it is classed asboolean
. That’s a property of the Structural Type System. More on this below.
A popular use-case for union types is to describe the set of
string
ornumber
literals that a value is allowed to be:
type WindowStates = "open" | "closed" | "minimized";
type LockStates = "locked" | "unlocked";
type PositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;
Unions provide a way to handle different types too. For example, you may have a function that takes an
array
or astring
:
function getLength(obj: string | string[]) {
return obj.length;
}
To learn the type of a variable, use
typeof
:
Type | Predicate |
---|---|
string | typeof s === "string" |
number | typeof n === "number" |
boolean | typeof b === "boolean" |
undefined | typeof undefined === "undefined" |
function | typeof f === "function" |
array | Array.isArray(a) |
For example, you can make a function return different values depending on whether it is passed a string or an array:
function wrapInArray(obj: string | string[]) {
if (typeof obj === "string") {
return [obj];
// (parameter) obj: string
}
return obj;
}
Generics
Generics provide variables to types. A common example is an array. An array without generics could contain anything. An array with generics can describe the values that the array contains.
type StringArray = Array<string>;
type NumberArray = Array<number>;
type ObjectWithNameArray = Array<{ name: string }>;
You can declare your own types that use generics:
interface Backpack<Type> {
add: (obj: Type) => void;
get: () => Type;
}
// This line is a shortcut to tell TypeScript there is a
// constant called `backpack`, and to not worry about where it came from.
declare const backpack: Backpack<string>;
// object is a string, because we declared it above as the variable part of Backpack.
const object = backpack.get();
// Since the backpack variable is a string, you can't pass a number to the add function.
backpack.add(23);
// Argument of type 'number' is not assignable to parameter of type 'string'.
Structural Type System
One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural typing”.
In a structural type system, if two objects have the same shape, they are considered to be of the same type.
interface Point {
x: number;
y: number;
}
function logPoint(p: Point) {
console.log(`${p.x}, ${p.y}`);
}
// logs "12, 26"
const point = { x: 12, y: 26 };
logPoint(point);
The
point
variable is never declared to be aPoint
type. However, TypeScript compares the shape ofpoint
to the shape ofPoint
in the type-check. They have the same shape, so the code passes.
The shape-matching only requires a subset of the object’s fields to match.
const point3 = { x: 12, y: 26, z: 89 };
logPoint(point3); // logs "12, 26"
const rect = { x: 33, y: 3, width: 30, height: 80 };
logPoint(rect); // logs "33, 3"
const color = { hex: "#187ABF" };
logPoint(color);
// Argument of type '{ hex: string; }' is not assignable to parameter of type 'Point'.
// Type '{ hex: string; }' is missing the following properties from type 'Point': x, y
There is no difference between how classes and objects conform to shapes:
class VirtualPoint {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
const newVPoint = new VirtualPoint(13, 56);
logPoint(newVPoint); // logs "13, 56"
If the object or class has all the required properties, TypeScript will say they match, regardless of the implementation details.
What is a tsconfig.json
Source: typescriptlang.org
The presence of a
tsconfig.json
file in a directory indicates that the directory is the root of a TypeScript project. Thetsconfig.json
file specifies the root files and the compiler options required to compile the project.
A project is compiled in one of the following ways using
tsconfig.json
orjsconfig.json
:
By invoking tsc with no input files, in which case the compiler searches for the
tsconfig.json
file starting in the current directory and continuing up the parent directory chain.By invoking tsc with no input files and a
--project
(or just-p
) command line option that specifies the path of a directory containing atsconfig.json
file, or a path to a valid.json
file containing the configurations.
Example tsconfig.json
files:
-
Using the
files
property{ "compilerOptions": { "module": "commonjs", "noImplicitAny": true, "removeComments": true, "preserveConstEnums": true, "sourceMap": true }, "files": [ "core.ts", "sys.ts", "types.ts", "scanner.ts", "parser.ts", "utilities.ts", "binder.ts", "checker.ts", "emitter.ts", "program.ts", "commandLineParser.ts", "tsc.ts", "diagnosticInformationMap.generated.ts" ] }
-
Using the
include
andexclude
properties{ "compilerOptions": { "module": "system", "noImplicitAny": true, "removeComments": true, "preserveConstEnums": true, "outFile": "../../built/local/tsc.js", "sourceMap": true }, "include": ["src/**/*"], "exclude": ["**/*.spec.ts"] }
TSConfig Bases
Depending on the JavaScript runtime environment which you intend to run your code in, there may be a base configuration which you can use at github.com/tsconfig/bases . These are
tsconfig.json
files which your project extends from which simplifies yourtsconfig.json
by handling the runtime support.For example, if you were writing a project which uses Node.js version 12 and above, then you could use the npm module
@tsconfig/node12
:
{
"extends": "@tsconfig/node12/tsconfig.json",
"compilerOptions": {
"preserveConstEnums": true
},
"include": ["src/**/*"],
"exclude": ["**/*.spec.ts"]
}
This lets your
tsconfig.json
focus on the unique choices for your project, and not all of the runtime mechanics. There are a few tsconfig bases already, and we’re hoping the community can add more for different environments.
tsc CLI Options
Using the CLI
Running
tsc
locally will compile the closest project defined by atsconfig.json
, or you can compile a set of TypeScript files by passing in a glob of files you want. When input files are specified on the command line,tsconfig.json
files are ignored.
# Run a compile based on a backwards look through the fs for a tsconfig.json
tsc
# Emit JS for just the index.ts with the compiler defaults
tsc index.ts
# Emit JS for any .ts files in the folder src, with the default settings
tsc src/*.ts
# Emit files referenced in with the compiler settings from tsconfig.production.json
tsc --project tsconfig.production.json
# Emit d.ts files for a js file with showing compiler options which are booleans
tsc index.js --declaration --emitDeclarationOnly
# Emit a single .js file from two files via compiler options which take string arguments
tsc app.ts util.ts --target esnext --outfile index.js
TypeScript - Integrating with Build Tool
Source: typescriptlang.org
Babel
Install
npm install @babel/cli @babel/core @babel/preset-typescript --save-dev
.babelrc
{
"presets": ["@babel/preset-typescript"]
}
Using Command Line Interface
./node_modules/.bin/babel --out-file bundle.js src/index.ts
package.json
{
"scripts": {
"build": "babel --out-file bundle.js main.ts"
},
}
Execute Babel from the command line
npm run build
Setting up eslint, prettier, airbnb-base and typescript
Source: medium.com
Create Config Files
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"baseUrl": "./",
"paths": {
"*": ["./src/*"]
},
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
TypeScript - Type Declarations
Source: typescriptlang.org
Throughout the sections you’ve read so far, we’ve been demonstrating basic TypeScript concepts using the built-in functions present in all JavaScript runtimes. However, almost all JavaScript today includes many libraries to accomplish common tasks. Having types for the parts of your application that aren’t your code will greatly improve your TypeScript experience. Where do these types come from?
What Do Type Declarations Look Like?
Let’s say you write some code like this:
const k = Math.max(5, 6);
const j = Math.mix(7, 8);
// Property 'mix' does not exist on type 'Math'.Property 'mix' does not exist on type 'Math'.
How did TypeScript know that
max
was present but notmix
, even thoughMath
’s implementation wasn’t part of your code?
The answer is that there are declaration files describing these built-in objects. A declaration file provides a way to declare the existence of some types or values without actually providing implementations for those values.
.d.ts
files
TypeScript has two main kinds of files.
.ts
files are implementation files that contain types and executable code. These are the files that produce.js
outputs, and are where you’d normally write your code.
.d.ts
files are declaration files that contain only type information. These files don’t produce.js
outputs; they are only used for typechecking. We’ll learn more about how to write our own declaration files later.
Built-in Type Definitions
TypeScript includes declaration files for all of the standardized built-in APIs available in JavaScript runtimes. This includes things like methods and properties of built-in types like
string
orfunction
, top-level names likeMath
andObject
, and their associated types. By default, TypeScript also includes types for things available when running inside the browser, such aswindow
anddocument
; these are collectively referred to as the DOM APIs.
TypeScript names these declaration files with the pattern
lib.[something].d.ts
. If you navigate into a file with that name, you can know that you’re dealing with some built-in part of the platform, not user code.
External Definitions
For non-built-in APIs, there are a variety of ways you can get declaration files. How you do this depends on exactly which library you’re getting types for.
Bundled Types
If a library you’re using is published as an npm package, it may include type declaration files as part of its distribution already. You can read the project’s documentation to find out, or simply try importing the package and see if TypeScript is able to automatically resolve the types for you.
If you’re a package author considering bundling type definitions with your package, you can read our guide on bundling type definitions .
DefinitelyTyped /@types
The DefinitelyTyped repository is a centralized repo storing declaration files for thousands of libraries. The vast majority of commonly-used libraries have declaration files available on DefinitelyTyped.
Definitions on DefinitelyTyped are also automatically published to npm under the
@types
scope. The name of the types package is always the same as the name of the underlying package itself. For example, if you installed thereact
npm package, you can install its corresponding types by running
npm install --save-dev @types/react
TypeScript automatically finds type definitions under
node_modules/@types
, so there’s no other step needed to get these types available in your program.
Your Own Definitions
In the uncommon event that a library didn’t bundle its own types and didn’t have a definition on DefinitelyTyped, you can write a declaration file yourself. See the appendix Writing Declaration Files for a guide.
If you want to silence warnings about a particular module without writing a declaration file, you can also quick declare the module as type
any
by putting an empty declaration for it in a.d.ts
file in your project. For example, if you wanted to use a module namedsome-untyped-module
without having definitions for it, you would write:
declare module "some-untyped-module";
Tim Oxley: The Structure of Node.JS Applications - JSConf.Asia 2015
Source: youtube.com
Slides: PDF
-
“Flat is better than nested”
Ask yourself “Can it be in a statement? If no, can it be a function? If no, can it be a file? If no, can it be a folder? If no, can it be an internal package? If no, can it be an external package?”
Examples:
- at statement level: avoid complex
if else
statements - at function level: avoid nested callbacks
- at class level: avoid multiple levels of inheritance
Give contributors commit access early to avoid “abandonware” ( openopensource.org ).
- at statement level: avoid complex
-
“Don’t get precious about your code.”
The first attempt is almost always wrong. Anecdote about code camp where they have to implement an algorithm 3 times over 3 days.
-
“Defer decisions”.
Try to make the least amount of important decisions as possible early. You will have a lot more information later. Just leave it for now.
The wrong structure can be more costly than no structure at all.
Structure can be more easily extracted from loose structure. Incorrect and complex structure needs to be torn down before building new structure.
Do the simplest thing that could possibly work Don’t write it if YAGNI
-
“Avoid premature modularisation.”
Abstractions decrease local complexity while increasing global complexity.
Wait until you use it three times before extracting it into a reusable function
Top-down programming (start by writing your main/entry function first)
Don’t design your code for extensibility, design your code so it can be deleted
More best practices: timoxley/best-practices