# Docs
# Miscellaneous
ES6 to earlier: babel.js (opens new window)
promisify and
this
const rl = readline.createInterface({ input: process.stdin }); function noerr_promisify(fun, ...defaults) { return (...arg) => new Promise((resolve) => { fun(...defaults, ...arg, ans => resolve(ans)); }); } const q = noerr_promisify(rl.question.bind(rl), ''); (async () => { const input = await q(); console.log(`resolved: ${input}`); await q().then(console.log); rl.close(); })();
string format
let soMany = 10; console.log(`This is ${soMany} times easier!`); // "This is 10 times easier!
- from ES6
- notice backticks in lieu of quotes
swap
b = [a, a=b][0]; [b, a] = [a, b]; // ES6
0
and-0
1 / x === 1 / Math.abs(x); Object.is(-0, +0) // ES6
- in
Math.atan()
- in
equivalent of
zip
in pythonzip = (...rows) => [...rows[0]].map((_,c) => rows.map(row => row[c])); // ES6
equivalent of
range
in pythonArray.apply(null, Array(5)).map(function (_, i) {return i;}); [...Array(10).keys()]; // ES6 Array.from({length: 10}, (x, i) => i); // ES6
shallow copy an array
new Array(...arr); // arr.length > 1; Array.of(...arr); arr.slice(0); [...arr]; Array.from(arr);
from fast to slow
floor division
(a/b | 0); // (a/b << 0)
# Lexical grammar
comments
#!
hashbang stage 3
literals
null
- boolean
- numbers
0x
,0o
,0b
, also capitalized0888
is 888,0777
is 511, not strict mode
- object
let iterable = { [Symbol.iterator]() { return { i: 0, next() { if (this.i < 3) { return { value: this.i++, done: false }; } return { value: undefined, done: true }; } }; } };
- array
- string
'\xA9' // "©", in the range 0x0000 to 0x00FF '\u00A9' // "©" (U+A9), For code points U+0000 to U+FFFF '\uD87E\uDC04' // larger range if two combined '\u{2F804}'
- regular expression
- template literals
Automatic semicolon insertion
# Statements
# Flow Control
blocks — used to group zero or more statements
- delimited by a pair of curly brackets
- and may optionally be labelled
empty — An empty statement is used to provide no statement, although the JavaScript syntax would expect one
;
break
— terminates the current loop, switch, or label statementcontinue
- also label
if...else
switch
===
- scope
const action = 'say_hello'; switch (action) { case 'say_hello': let message = 'hello'; console.log(message); break; case 'say_hi': let message = 'hi'; console.log(message); break; default: console.log('Empty action received.'); break; }
- result —
Uncaught SyntaxError: Identifier 'message' has already been declared
- fix — add brackets
- result —
- expression condition
switch (true) { case num < 0: alert(“Less than 0.”); break; case num >= 0 && num <= 10: alert(“Between 0 and 10.”); break; case num > 10 && num <= 20: alert(“Between 10 and 20.”); break; default: alert(“More than 20.”); }
try...catch
,finally
- cannot
break
when unlabelled - catch by type
try { myroutine(); // may throw three types of exceptions } catch (e) { if (e instanceof TypeError) { // statements to handle TypeError exceptions } else if (e instanceof RangeError) { // statements to handle RangeError exceptions } else if (e instanceof EvalError) { // statements to handle EvalError exceptions } else { // statements to handle any unspecified exceptions logMyErrors(e); // pass exception object to error handler } }
- optional catch binding — stage 4
try { // ... } catch { // ... }
finally
return value surpasses return value intry
andcatch
console.log((() => { try { return 1; } finally { return 2; } })()); // 2
- cannot
throw
- throw expression or
new Error
- throw expression or
# Declarations
var
- scope — current execution context (function)
- variable hoisting
- Undeclared variables are always global, Undeclared variables do not exist until the code assigning to them is executed, Undeclared variables are configurable (e.g. can be deleted)
function x() { y = 1; // Throws a ReferenceError in strict mode var z = 2; } x(); console.log(y); // logs "1" console.log(z); // Throws a ReferenceError: z is not defined outside x console.log(a); // ReferenceError delete this.x; // Throws a TypeError in strict mode. Fails silently otherwise. delete this.y; // true
let
- temporal dead zone
// prints out 'undefined' console.log(typeof undeclaredVariable); // results in a 'ReferenceError', TDZ console.log(typeof i); let i = 10; n = {a: [1, 2, 3]}; for (let n of n.a) { // ReferenceError, TDZ console.log(n); }
- temporal dead zone
const
# function and class
function
- variable hoisting
- Conditionally created functions — results are inconsistent across implementations
- function expressions are not hoisted
function*
— returnsGenerator
objectGeneratorFunction
constructorconst GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor
- generator function created with the
GeneratorFunction
constructor do not create closures to their creation contexts; they always are created in the global scope
- generator function created with the
async function
— returns anAsyncFunction
objectAsyncFunction
constructorconst AsyncFunction = Object.getPrototypeOf(async function(){}).constructor
- async functions created with the
AsyncFunction
constructor do not create closures to their creation contexts; they are always created in the global scope
- async functions created with the
return
class
- unlike the class expression, the class declaration doesn't allow an existing class to be declared again and will throw a type error if attempted.
- The class body of a class declaration is executed in strict mode
- The constructor property is optional.
- not hoisted
# Iterations
do...while
while
for
for
without a statementfor ( var oItNode = document.getElementById(sId); /* initialization */ oItNode; /* condition */ nLeft += oItNode.offsetLeft, nTop += oItNode.offsetTop, oItNode = oItNode.offsetParent /* final-expression */ ); /* semicolon, mandatory here */
for...in
— iterates over all non-Symbol, enumerable properties of an object- arbitrary order
- Iterating over own properties only
type PropertyKey = string | number | symbol
, number will be converted to stringObject.prototype.hasOwnProperty(v: PropertyKey): boolean
Object.prototype.propertyIsEnumerable(v: PropertyKey): boolean
Object.getOwnPropertyNames(o: any): string[]
Object.keys(o: any): string[]
,Object.entries()
,Object.values()
for...of
—Symbol.iterator
hookfor await...of
—Symbol.asyncIterator
hook, also sync iterables
# Others
debugger
import
- hoist
- strict mode
export
- strict mode
import.meta
— a meta-property exposing context-specific meta data to a JavaScript module
# Expressions and operators
# Primary expressions
this
# Typed Arrays
Typed Arrays
- WebGL deals with complex calculations requiring predictable precision, standard JavaScript numbers do not work
- WebGL introduces the concept of typed arrays, which are arrays whose items are set to be values of a particular type
ArrayBuffer
- a generic, fixed-length raw binary data buffer
- cannot directly manipulate the contents
- create one of the typed array objects or a
DataView
object which represents the buffer in a specific format, and use that to read and write the contents of the buffer - constructor:
ArrayBuffer(byteLength: number)
ArrayBuffer.prototype.byteLength
Read only — The size, in bytes, of the array
Views —
DataView
- An array buffer view is a particular way of using the bytes within an array buffer
- constructor:
DataView(buffer: ArrayBuffer, byteOffset?: number, byteLength?)
- properties: from constructor
DataView.prototype.buffer
Read only — The ArrayBuffer referenced by this view. Fixed at construction time and thus read onlyDataView.prototype.byteLength
Read only — The length (in bytes) of this view from the start of its ArrayBuffer. Fixed at construction time and thus read onlyDataView.prototype.byteOffset
Read only — The offset (in bytes) of this view from the start of its ArrayBuffer. Fixed at construction time and thus read only
- read methods: return a number
DataView.prototype.getInt8(byteOffset)
— Gets a signed 8-bit integer (byte) at the specified byte offset from the start of the viewDataView.prototype.getUint8(byteOffset)
— Gets an unsigned 8-bit integer (unsigned byte) at the specified byte offset from the start of the viewDataView.prototype.getInt16(byteOffset, littleEndian?: boolean)
— Gets a signed 16-bit integer (short) at the specified byte offset from the start of the viewDataView.prototype.getUint16(byteOffset, littleEndian?: boolean)
— Gets an unsigned 16-bit integer (unsigned short) at the specified byte offset from the start of the viewDataView.prototype.getInt32(byteOffset, littleEndian?: boolean)
— Gets a signed 32-bit integer (long) at the specified byte offset from the start of the viewDataView.prototype.getUint32(byteOffset, littleEndian?: boolean)
— Gets an unsigned 32-bit integer (unsigned long) at the specified byte offset from the start of the viewDataView.prototype.getFloat32(byteOffset, littleEndian?: boolean)
— Gets a signed 32-bit float (float) at the specified byte offset from the start of the viewDataView.prototype.getFloat64(byteOffset, littleEndian?: boolean)
— Gets a signed 64-bit float (double) at the specified byte offset from the start of the view
- write methods
DataView.prototype.setInt8(byteOffset, value)
— Stores a signed 8-bit integer (byte) value at the specified byte offset from the start of the viewDataView.prototype.setUint8(byteOffset, value)
— Stores an unsigned 8-bit integer (unsigned byte) value at the specified byte offset from the start of the viewDataView.prototype.setInt16(byteOffset, value, littleEndian?)
— Stores a signed 16-bit integer (short) value at the specified byte offset from the start of the viewDataView.prototype.setUint16(byteOffset, value, littleEndian?)
— Stores an unsigned 16-bit integer (unsigned short) value at the specified byte offset from the start of the viewDataView.prototype.setInt32(byteOffset, value, littleEndian?)
— Stores a signed 32-bit integer (long) value at the specified byte offset from the start of the viewDataView.prototype.setUint32(byteOffset, value, littleEndian?)
— Stores an unsigned 32-bit integer (unsigned long) value at the specified byte offset from the start of the viewDataView.prototype.setFloat32(byteOffset, value, littleEndian?)
— Stores a signed 32-bit float (float) value at the specified byte offset from the start of the viewDataView.prototype.setFloat64(byteOffset, value, littleEndian?)
— Stores a signed 64-bit float (double) value at the specified byte offset from the start of the view
Typed Views
Type Value Range Description Web IDL type Equivalent C type Int8Array
-128 to 127 8-bit two's complement signed integer byte int8_t
Uint8Array
0 to 255 8-bit unsigned integer octet uint8_t
Uint8ClampedArray
0 to 255 8-bit unsigned integer (clamped) octet uint8_t
Int16Array
-32768 to 32767 16-bit two's complement signed integer short int16_t
Uint16Array
0 to 65535 16-bit unsigned integer unsigned short uint16_t
Int32Array
-2147483648 to 2147483647 32-bit two's complement signed integer long int32_t
Uint32Array
0 to 4294967295 32-bit unsigned integer unsigned long uint32_t
Float32Array
1.2x10-38 to 3.4x1038 32-bit IEEE floating point number ( 7 significant digits e.g. 1.1234567) unrestricted float float
Float64Array
5.0x10-324 to 1.8x10308 64-bit IEEE floating point number (16 significant digits e.g. 1.123...15) unrestricted double double
act like regular arrays with the exception that their elements must be of a particular data type
- methods of
Array
are available, and more - when setting, the number is stored as the modulo of the largest possible number
- additional method:
TypedArray.prototype.subarray(begin, end): TypedArray
- difference with
slice()
: this is creating a new view on the existing buffer; changes to the new object's contents will impact the original object and vice versa - begin and end is clamped to the valid index range for the current array; if the computed length of the new array would be negative, it's clamped to zero. If either begin or end is negative, it refers to an index from the end of the array instead of from the beginning
- difference with
- methods of
inherits:
DataView
constructor
new TypedArray(); // new in ES2017 new TypedArray(length); new TypedArray(typedArray); new TypedArray(object); new TypedArray(buffer [, byteOffset [, length]]);
properties of constructors
name
: constructor namelength
BYTES_PER_ELEMENT
: help determine length when build fromArrayBuffer
The contents are initialized to 0
# Advanced???
- Advanced Functions: ???
Safe Type Detection
function isArray(value) { return Object.prototype.toString.call(value) == "[object Array]"; // false for COM }
largely in use for identifying the native or library
Scope-Safe Constructors: prevent call without
new
function Person(name, age, job) { if (this instanceof Person) { this.name = name; this.age = age; this.job = job; } else { return new Person(name, age, job); } }
under this circumstance constructor stealing must be used with prototype chaining or parasitic combination
Lazy Loading Functions
manipulating the function the first time it is called
function createXHR() { if (typeof XMLHttpRequest != "undefined") { createXHR = function () { return new XMLHttpRequest(); }; } }
return a new anonymous function
Function.prototype.bind(thisArg: any, ...argArray: any[])
— creates a new bound function (BF)- creates a new function that, when called, has its
this
keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called- more overhead
- thisArg
- The value is ignored if the bound function is constructed using the
new
operator - When using bind to create a function(supplied as a callback) inside a
setTimeout()
, any primitive value passed asthisArg
is converted to object - If no arguments are provided to bind, the
this
of the executing scope is treated as thethisArg
for the new function
- The value is ignored if the bound function is constructed using the
- internally when without
new
—Function.prototype.call([[BoundThis]], [[BoundArguments]], args)
- example
var slice = Array.prototype.slice; slice.apply(arguments); var unboundSlice = Array.prototype.slice; var slice = Function.prototype.apply.bind(unboundSlice); slice(arguments);
- creates a new function that, when called, has its
# ECMAScript
# Object
- Tamper-Proof Objects
- for individual properties: manually set each property’s
[[Configurable]]
,[[Writable]]
,[[Enumerable]]
,[[Value]]
,[[Get]]
, and[[Set]]
attributes to alter how the property behaves - once an object has been made tamper-proof, the operation cannot be undone
- corresponding modification fails silently or by throwing a
TypeError
(most commonly, but not exclusively, when in strict mode) Object.preventExtensions(o: any): any
— Prevents any extensions of an object__proto__
also become immutable
Object.isExtensible(o: any): boolean
— Determines if extending of an object is allowedObject.seal(o: any): any
— seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable- non-extensible and
[[configurable]]
set tofalse
- non-extensible and
Object.isSealed(o: any): boolean
— Determines if an object is sealedObject.freeze(o: any): any
— freezes an object: that is, prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed, it also prevents the prototype from being changed- non-extensible, sealed and property value cannot be changed
Object.isFrozen(o: any): boolean
— Determines if an object was frozen
- for individual properties: manually set each property’s
# Error Handling
# try catch and errors
Syntax
try { // } catch (error) { // } finally { // }
- Unlike other languages, you must define a name for the
Error
object even if you don’t intend to use it - nothing that can be done in the
try
orcatch
portion of the statement to prevent the code in finally from executing, which includes using areturn
statement - If
finally
is provided, thencatch
becomes optional, and vice versa- IE7 and earlier had a bug where the code in
finally
would never be executed unless there was also acatch
- IE7 and earlier had a bug where the code in
- Unlike other languages, you must define a name for the
Error
- constructor:
Error(message?: string)
- can be used as a function (without
new
)
- can be used as a function (without
- properties and methods
Error.prototype.message
— Error messageError.prototype.name
— Error nameError.prototype.toString()
— Returns a string (Error: ${error.message}
) representing the specified object. Overrides theObject.prototype.toString()
method
- nonstandard but widely support properties
Error.prototype.stack
— Stack trace
- constructor:
Error Types
try { foo.bar(); } catch (e) { if (e instanceof ReferenceError) { console.log(e.name + ': ' + e.message); } else if (e instanceof RangeError) { console.log(e.name + ': ' + e.message); } // ... etc }
- constructor name is the same as error type name, and similar to
Error()
EvalError
— Creates an instance representing an error that occurs regarding the global functioneval()
- This exception is not thrown by JavaScript anymore, however the
EvalError
object remains for compatibility
- This exception is not thrown by JavaScript anymore, however the
RangeError
— Creates an instance representing an error that occurs when a numeric variable or parameter is outside of its valid rangeReferenceError
— Creates an instance representing an error when a non-existent variable is referencedSyntaxError
— Creates an instance representing a syntax error when trying to interpret syntactically invalid codeTypeError
— Creates an instance representing an error that occurs when a variable or parameter is not of a valid typeURIError
— Creates an instance representing an error that occurs whenencodeURI()
ordecodeURI()
are passed invalid parameters
- constructor name is the same as error type name, and similar to
Custom Error Types
ES6
class CustomError extends Error { constructor(foo = 'bar', ...params) { // Pass remaining arguments // (including vendor specific ones) to parent constructor super(...params); // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, CustomError); } // Custom debugging information this.foo = foo; this.date = new Date(); } } try { throw new CustomError('baz', 'bazMessage'); } catch (e) { console.log(e.foo); //baz console.log(e.message); //bazMessage console.log(e.stack); //stacktrace }
- Babel and other transpilers will not correctly handle the code without additional configuration
- Some browsers include the CustomError constructor in the stack trace when using ES2015 classes
ES5
function CustomError(foo, message, fileName, lineNumber) { var instance = new Error(message, fileName, lineNumber); instance.foo = foo; Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); if (Error.captureStackTrace) { Error.captureStackTrace(instance, CustomError); } return instance; } CustomError.prototype = Object.create(Error.prototype, { constructor: { value: Error, enumerable: false, writable: true, configurable: true } }); if (Object.setPrototypeOf) { Object.setPrototypeOf(CustomError, Error); } else { CustomError.__proto__ = Error; } try { throw new CustomError('baz', 'bazMessage'); } catch (e) { console.log(e.foo); //baz console.log(e.message);//bazMessage }
All browsers include the CustomError constructor in the stack trace when using a prototype declaration
# throw, console
throw
syntaxthrow expression;
- any expression can be thrown, but better an object, especially using specific type constructor
- if a caught error cannot be handled, consider rethrow
3 common errors
- Type Coercion Errors: avoid
==
,!=
,if (variable)
- Data Type Errors: use
typeof
,instanceof
- Communication Errors: The
encodeURIComponent()
method should always be used for query string arguments
- Type Coercion Errors: avoid
Log Errors to the Server
- use
Image()
???function logError(sev, msg) { var img = new Image(); img.src = "log.php?sev=" + encodeURIComponent(sev) + "&msg=" + encodeURIComponent(msg); }
- use
Console
provides access to the browser's debugging console,
window.console
print methods
Console.log(...)
— For general output of logging information- when logging objects in the latest versions of Chrome and Firefox what get logged on the console is a reference to the object
- use
console.log(JSON.parse(JSON.stringify(obj)))
to see the value at the moment of being logged - prints the element in an HTML-like tree for
Node
objects - string substitution (opens new window) (%) and additional arguments
Console.assert()
(no string substitution),Console.debug()
,Console.error()
,Console.info()
,Console.warn()
Console.debug(...)
— Outputs a message to the console (Console.log()
) with the log level "debug"- Note: Starting with Chromium 58 this method only appears in Chromium browser consoles when level "Verbose" is selected.
Console.error(...)
—Console.log()
with error levelConsole.info(...)
—Console.log()
Console.warn(...)
—Console.log()
with warning levelConsole.assert(assertion?: boolean, ...): void
— Log a error level message and stack trace to console if the first argument isfalse
Console.log()
with a condition and error level and without string substitution
Console.dir(obj): void
— Displays an interactive list of the properties of the specified JavaScript object- prints the
HTMLElement
in a JSON-like tree
- prints the
Console.dirxml(obj)
—Console.dir()
which displays an interactive tree of the descendant elements of the specified XML/HTML element if possibleConsole.table(data, columns?): void
— logs data as a table. Each element in the array (or enumerable property if data is an object) will be a row in the table- columns parameter to select a subset of columns to display
Console.clear()
— Clear the console
count methods
Console.count(label?: string)
— Log the number of times this line or a particular call has been called with the given label- without the parameter, the label is
"default"
- without the parameter, the label is
Console.countReset(label?: string)
— Resets the value of the counter with the given label
methods with new indentation
Console.group(groupTitle?)
— Creates a new inline group, indenting all following output by another levelConsole.groupCollapsed(groupTitle?)
— Creates a new inline group, indenting all following output by another level. However, unlikegroup()
this starts with the inline group collapsed requiring the use of a disclosure button to expand itConsole.groupEnd()
— Exits the current inline group
timing methods
Console.time(label?: string)
— Starts a timer with a name specified as an input parameter- Up to 10,000 simultaneous timers can run on a given page
- label defaults to
"default"
Console.timeEnd(label?)
— Stops the specified timer and logs the elapsed time in seconds since it startedConsole.timeLog(label?)
— Logs the value of the specified timer to the console- not widely supported
Console.timeStamp(label?: string)
nonstandard — Adds a marker to the browser's Timeline or Waterfall tool- label will then be shown alongside the marker
Console.trace()
— Outputs a stack traceStyling console output — You can use the
%c
directive to apply a CSS style to console output:console.log("This is %cMy stylish message", "color: yellow; font-style: italic; background-color: blue;padding: 2px");
Note: Quite a few CSS properties are supported by this styling; you should experiment and see which ones prove useful
# Maintainability, Performance and Deployment
maintainability
four spaces and semicolon
comments
intuitive and proper naming
var name
will overridewindow.name
type transparency
/** * @param {string} s * @param * @return {number} */
loose coupling
- decouple JavaScript inline in HTML
- decouple
HTMLElement.style
by alteringHTMLElement.className
- decouple Application Logic/Event Handlers
Respect Object Ownership
Avoid Globals
Avoid Null Comparisons
Use Constants
performance
- Be Scope-Aware — avoid long scope-chain lookup ???
- Avoid long property lookup
- actually long chaining dict lookup
- Avoid Double Interpretation
- prefer native methods, switch statements and bitwise operations
- Optimize DOM Interactions — DOM manipulations and interactions take a large amount of time because they often require rerendering all or part of the page ???
- Minimize Live Updates
- use
DocumentFragment
- use
HTMLElement.InnerHTML
- use
- Use Event Delegation
- Beware of
HTMLCollections
(andNodeList
) — AnHTMLCollection
in the HTML DOM is live; it is automatically updated when the underlying document is changed- cache
length
and items in loops - In some cases,
NodeList
is static, where any changes in the DOM does not affect the content of the collection, asDocument.querySelectorAll()
- In other cases, the
NodeList
is live, which means that changes in the DOM automatically update the collection, such asNode.childNodes
- cache
- Minimize Live Updates
deployment
- webpack
- build
- validation
- compression — file and HTTP
# JSON
syntax
- simple values — Strings, numbers, Booleans, and
null
, noundefined
- strings must use double quotes to be valid
- objects — key-value pairs
- key is enclosed by double quotes
- no trailing semicolon
- arrays
- simple values — Strings, numbers, Booleans, and
polyfill
- tbc
- better not use
eval()
JSON
JSON.parse(text: string, receiver?: (key: any, value: any) => any): any
- text: The string to parse as JSON
- reviver — If a function, this prescribes how the value originally produced by parsing is transformed, before being returned
- does not allow trailing commas:
SyntaxError
JSON.stringify(value: any, replacer?: (key: string, value: any) => any, space?: string | number): string
JSON.stringify(value: any, replacer?: (string | number)[], space?: string | number): string
- value — The value to convert to a JSON string
- replacer — A function that alters the behavior of the stringification process, or an array of
String
andNumber
objects that serve as a whitelist for selecting/filtering the properties of the value object to be included in the JSON string- If you return any other object, the object is recursively stringified into the JSON string, calling the replacer function on each property, unless the object is a function, in which case nothing is added to the JSON string.
- If you return
undefined
, the property is not included (i.e., filtered out) in the output JSON string. - cannot use the replacer function to remove values from an array. If you return
undefined
or a function thennull
is used instead
- space
- if a
Number
, it indicates the number of space characters to use as white space; this number is capped at 10 and 0 - if a string, the string (or the first 10 characters of the string) is used as whitespace
- if a
- rule
- If the value has a
toJSON()
method, it's responsible to define what data will be serialized Boolean
,Number
, andString
objects are converted to the corresponding primitive values during stringification, in accord with the traditional conversion semantics- If
undefined
, aFunction
, or aSymbol
is encountered during conversion it is either omitted (when it is found in an object) or censored tonull
(when it is found in an array).JSON.stringify()
can also just return undefined when passing in "pure" values likeJSON.stringify(function(){})
orJSON.stringify(undefined)
- All
Symbol
-keyed properties will be completely ignored, even when using the replacer function - The instances of Date implement the
toJSON()
function by returning a string (the same asdate.toISOString())
. Thus, they are treated as strings - The numbers
Infinity
andNaN
, as well as the objectnull
, are all considerednull
- All the other
Object
instances (includingMap
,Set
,WeakMap
, andWeakSet
) will have only their enumerable properties serialized
- If the value has a
# Strict Mode
Invoking strict mode
- put the exact statement
"use strict";
(or'use strict';
) before any other statements- for a script or a function
- for scripts
- no effect for functions inside
- concatenate conflicting scripts can be problematic
- modules are automatically in strict mode, with no statement needed to initiate it
- put the exact statement
variables
- assigning a value to an undeclared variable throws a
ReferenceError
- an attempt to
delete
a variable causes anReferenceError
— Nonstrict mode allows this and may silently fail (returningfalse
) - disallows variables named
implements
,interface
,let
,package
,private
,protected
,public
,static
, andyield
, causesSyntaxError
- also function names
- disallows using
eval
andarguments
as identifiers and manipulating their values orSyntaxError
- assigning a value to an undeclared variable throws a
Objects
- Assigning a value to a read-only property throws a
TypeError
- Using delete on a nonconfigurable property throws a
TypeError
- Attempting to add a property to a nonextensible object throws a
TypeError
- When using an object literal, property names must be unique or throws
SyntaxError
— not ES6
- Assigning a value to a read-only property throws a
Functions
- named function arguments be unique or
SyntaxError
- In nonstrict mode, changes to a named argument are also reflected in the
arguments
object, whereas strict mode ensures that each are completely separate - the elimination of
arguments.callee
andarguments.caller
, throws aTypeError
- disallowing function declarations unless they are at the top level of a script or function, or
SyntaxError
- function statements outside top level are permitted in ES6
- named function arguments be unique or
eval()
- no longer create variables or functions in the containing context
- what declared inside remain inside a special scope that is used while code is being evaluated and then destroyed once completed
- no longer create variables or functions in the containing context
this
- in nonstrict mode, When using the
apply()
orcall()
methods of a function, anull
orundefined
value is coerced to the global object - In strict mode, the
this
value for a function is always used as specified - and if unspecified,
this
will beundefined
- in nonstrict mode, When using the
miscellanea
- An attempt to use
with
in strict mode results in aSyntaxError
- An octal literal is now considered invalid syntax in strict mode
parseInt()
with octal literals are considered decimal literals with a leading zero- not ES6
0o
- ES6 forbids setting properties on primitive values or
TypeError
- An attempt to use
# Build Tools
webpack
# TypeScript — superset of JavaScript
# CLI
ts-node
.type
in REPL
tsc
tsconfig.json
use
- 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 a tsconfig.json file, or a path to a valid .json file containing the configurations
Example 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" ] }
example Using the
"include"
and"exclude"
propertiesstrict example
{ "compilerOptions": { "alwaysStrict": true, // Parse in strict mode and emit "use strict" for each source file. // If you have wrong casing in referenced files e.g. the filename is Global.ts and you have a /// <reference path="global.ts" /> to reference this file, then this can cause to unexpected errors. Visite: http://stackoverflow.com/questions/36628612/typescript-transpiler-casing-issue "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file. // "allowUnreachableCode": false, // Do not report errors on unreachable code. (Default: False) // "allowUnusedLabels": false, // Do not report errors on unused labels. (Default: False) "noFallthroughCasesInSwitch": true, // Report errors for fall through cases in switch statement. "noImplicitReturns": true, // Report error when not all code paths in function return a value. "noUnusedParameters": true, // Report errors on unused parameters. "noUnusedLocals": true, // Report errors on unused locals. "noImplicitAny": true, // Raise error on expressions and declarations with an implied "any" type. "noImplicitThis": true, // Raise error on this expressions with an implied "any" type. "strictNullChecks": true, // The null and undefined values are not in the domain of every type and are only assignable to themselves and any. // To enforce this rules, add this configuration. "noEmitOnError": true // Do not emit outputs if any errors were reported. } }
Project References — tbc
# Lint
-
- rules (opens new window)
tslint --help
tslint --init
# Type Basics
Basic Types
concrete values
boolean
number
string
Array<T>
orT[]
ReadonlyArray<T>
tuple — express an array where the type of a fixed number of elements is known
- When accessing an element outside the set of known indices, a union type is used instead:
T1 | T2
[T1, T2]
?
—[number, string?, boolean?];
- When accessing an element outside the set of known indices, a union type is used instead:
any
— opt-out of type-checking and let the values pass through compile-time checksunknown
- Anything is assignable to
unknown
, but unknown isn’t assignable to anything but itself andany
without a type assertion or a control flow based narrowing - Likewise, no operations are permitted on an unknown without first asserting or narrowing to a more specific type.
- Anything is assignable to
void
— return type of functions, orundefined
ornull
null
andundefined
— subtypes of all other types when without--strictNullChecks
flag- use union type:
string | null | undefined
- use union type:
never
— the return type for a function expression or an arrow function expression that always throws an exception or one that never returns- Variables also acquire the type
never
when narrowed by any type guards that can never be true - a subtype of, and assignable to, every type
any
is not assignable tonever
- Variables also acquire the type
object
,<T>{}
(T
forinterface
),{a: string, b?: number}
,{a: string; b?: number}
C-like
enum
— members as properties, enum name as typeenum Color {Red, Green, Blue} let c: Color = Color.Green; enum Color {Red = 1, Green = 2, Blue = 4} let c: Color = Color.Green; enum Color {Red = 1, Green, Blue} let colorName: string = Color[2];
computed cannot be followed by non-initialized
enum E { A = getSomeValue(), B, // error! 'A' is not constant-initialized, so 'B' needs an initializer }
String enums — each member has to be constant-initialized with a string literal, or with another string enum member
enum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT", }
Heterogeneous enums — not recommended
When all members in an enum have literal enum values
enum members as types
enum ShapeKind { Circle, Square, } interface Circle { kind: ShapeKind.Circle; radius: number; } interface Square { kind: ShapeKind.Square; sideLength: number; } let c: Circle = { kind: ShapeKind.Square, // ~~~~~~~~~~~~~~~~ Error! radius: 100, }
enum types themselves effectively become a union of each enum member
Enums at runtime
- real objects that exist at runtime
- Reverse mappings — numeric enums members also get a reverse mapping from enum values to enum names
enum Enum { A } let a = Enum.A; let nameOfA = Enum[a]; // "A"
const
enums — can only use constant enum expressions, completely removed during compilation, inlined at use sitesAmbient enums
declare enum
— describe the shape of already existing enum types- an ambient (and non-const) enum member that does not have initializer is always considered computed
variable declaration
declare
— declare variables that may not have originated from a TypeScript file, not for compilation and simply a hint to the compiler.d.ts
type
: liketypedef
in Ctype C = { a: string, b?: number } function f({ a, b }: C): void { // ... } type Tree<T> = { value: T; left: Tree<T>; right: Tree<T>; } type LinkedList<T> = T & { next: LinkedList<T> }; interface Person { name: string; } var people: LinkedList<Person>;
function type
let myAdd: (baseValue: number, increment: number) => number = function(x: number, y: number): number { return x + y; }; let f: (x: number) => number = x => x * x; let f2: (x: number) => <number> x * x; function foo(...args: [number, string, boolean]): void; function foo(args_0: number, args_1: string, args_2: boolean): void;
default-initialized parameters don’t need to occur after required parameters, unlike ES6
this
parameters — fake parameters that come first in the parameter list, makethis
a explicit type in lieu ofany
this: void
means a function that does not require athis
type
this
parameters in callbacksinterface UIElement { addClickListener(onclick: (this: void, e: Event) => void): void; } class Handler0 { info: string; onClickBad(this: Handler, e: Event) { // oops, used this here. using this callback would crash at runtime this.info = e.message; } } class Handler { info: string; onClickGood = (e: Event) => { this.info = e.message } } let h = new Handler(); uiElement.addClickListener(h.onClickBad);
- arrow functions don’t capture
this
, so you can always pass them to something that expectsthis: void
- one arrow function is created per object of type Handler. Methods, on the other hand, are only created once and attached to Handler’s prototype
- arrow functions don’t capture
overload
function pickCard(x: {suit: string; card: number; }[]): number; function pickCard(x: number): {suit: string; card: number; }; function pickCard(x): any { // Check to see if we're working with an object/array // if so, they gave us the deck and we'll pick the card if (typeof x == "object") { let pickedCard = Math.floor(Math.random() * x.length); return pickedCard; } // Otherwise just let them pick the card else if (typeof x == "number") { let pickedSuit = Math.floor(x / 13); return { suit: suits[pickedSuit], card: x % 13 }; } }
type assertion
let someValue: any = "this is a string"; let strLength: number = (<string>someValue).length; let strLength2: number = (someValue as string).length;
- no runtime impact, and is used purely by the compiler
null
orundefined
removing type assertion —!
:str!.charAt(0)
type inference
type compatibility — Structural typing is a way of relating types based solely on their members
- x is compatible with y if y has at least the same members as x
destructuring
let { a, b }: { a: string, b: number } = o;
- default values
function keepWholeObject(wholeObject: { a: string, b?: number }) { let { a, b = 1001 } = wholeObject; }
- default values
# Interfaces
interface
—type
for objects, only the shape that mattersinterface LabelledValue { label: string; op?: number; readonly ro?: string; } function printLabel(labelledObj: LabelledValue) { console.log(labelledObj.label); } let myObj = {size: 10, label: "Size 10 Object"}; printLabel(myObj);
optional properties:
?
readonly
Excess Property Checks
let s2: LabelledValue = myObj; // OK, but not recommended let s3: LabelledValue = {size: 10, label: '111'} // error: size does not exist in type LabelledValue
get around — type assertion
Indexable type — an index signature that describes the types we can use to index into the object: config the interface to be able to have some extra properties
interface SquareConfig { color?: string; width?: number; [propName: string]: any; }
- two types of supported index signatures: string and number
- the type returned from a numeric indexer must be a subtype of the type returned from the string indexer,
a[100]
is the same asa['100']
in JavaScript - string index enforce all properties match their return type
interface
for functionsinterface SearchFunc { (source: string, subString: string): boolean; // call signature }
the names of the parameters do not need to match
let mySearch: SearchFunc; mySearch = function(src: string, sub: string): boolean { let result = src.search(sub); return result > -1; }
can infer the argument types
let mySearch: SearchFunc; mySearch = function(src, sub) { let result = src.search(sub); return result > -1; }
interface
forclass
implements
interface ClockInterface { currentTime: Date; setTime(d: Date); } class Clock implements ClockInterface { currentTime: Date; setTime(d: Date) { this.currentTime = d; } constructor(h: number, m: number) { } }
- can also describe methods
- describe the public side of the class, rather than both the public and private side
- only the instance side of the class is checked, not the static side
- create an interface with a construct signature and try to create a class that implements this interface you get an error
- also check constructor: refer to docs
Extending Interfaces
interface Shape { color: string; } interface PenStroke { penWidth: number; } interface Square extends Shape, PenStroke { sideLength: number; } let square = <Square>{}; square.color = "blue"; square.sideLength = 10; square.penWidth = 5.0;
- interfaces can extend each other. This allows you to copy the members of one interface into another
Interfaces Extending Classes — inherits the members of the class but not their implementations
- even inherits the private and protected members of a base class
class Control { private state: any; } interface SelectableControl extends Control { select(): void; } class Button extends Control implements SelectableControl { select() { } } // Error: Property 'state' is missing in type 'Image'. class myImage implements SelectableControl { select() { } }
- even inherits the private and protected members of a base class
Hybrid Types
- for example, an object that acts as both a function and an object, with additional properties
interface Counter { (start: number): string; interval: number; reset(): void; } function getCounter(): Counter { let counter = <Counter>function (start: number) { }; counter.interval = 123; counter.reset = function () { }; return counter; } let c = getCounter(); c(10); c.reset(); c.interval = 5.0;
- for example, an object that acts as both a function and an object, with additional properties
# Classes
class
class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; } } let greeter = new Greeter("world");
extends
super()
— the constructor of the base class- before we ever access a property on this in a constructor body, we have to call
super()
. This is an important rule that TypeScript will enforce
- before we ever access a property on this in a constructor body, we have to call
- override method — use the same identifier
Public, private, protected and more
Public by default
private
andprotected
— the same declaration is required when comparing typeclass Animal { private name: string; constructor(theName: string) { this.name = theName; } } class Rhino extends Animal { constructor() { super("Rhino"); } } class Employee { private name: string; constructor(theName: string) { this.name = theName; } } let animal = new Animal("Goat"); let rhino = new Rhino(); let employee = new Employee("Bob"); animal = rhino; animal = employee; // Error: 'Animal' and 'Employee' are not compatible
private
— cannot be accessed from outside of its containing classprotected
— likeprivate
but can also be accessed within deriving classes- A constructor may also be marked
protected
- A constructor may also be marked
readonly
Parameter properties — constructor parameter with
readonly
,public
,private
orprotected
, will be initialized as properties of the classaccessors —
get
,set
as in ES6 with type- accessors with a
get
and noset
are automatically inferred to be readonly
- accessors with a
static
: use the class name in lieu ofthis
to access
Abstract Classes —
abstract
abstract class Animal { abstract makeSound(): void; move(): void { console.log("roaming the earth..."); } }
- for other classes to derive, may not be instantiated directly
- Unlike an interface, an abstract class may contain implementation details for its members
- may optionally include access modifiers
abstract
marked do not contain an implementation and must be implemented in derived classes- objects declared abstract class type but instantiated by subclass constructor, will lose what defined only in that subclass
use class as an interface
- a class declaration creates two things: a type representing instances of the class and a constructor function
- Because classes create types, you can use them in the same places you would be able to use interfaces
# Mixins
concept — another popular way of building up classes from reusable components is to build them by combining simpler partial classes
- wikipedia — a Mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes
- sometimes described as being "included" rather than "inherited", can also be viewed as an interface with implemented methods
- Mixins encourage code reuse and can be used to avoid the inheritance ambiguity that multiple inheritance can cause (the "diamond problem")
example
// Disposable Mixin class Disposable { isDisposed: boolean; dispose() { this.isDisposed = true; } } // Activatable Mixin class Activatable { isActive: boolean; activate() { this.isActive = true; } deactivate() { this.isActive = false; } } @mixin([Disposable, Activatable]) class SmartObject implements Disposable, Activatable { constructor() { setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500); } interact() { this.activate(); } // Disposable isDisposed: boolean = false; dispose: () => void; // Activatable isActive: boolean = false; activate: () => void; deactivate: () => void; } // alternative — Object.assign() function mixin(baseCtors: any[]) { return function applyMixins(derivedCtor: any) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { derivedCtor.prototype[name] = baseCtor.prototype[name]; }); }); } } let smartObj = new SmartObject(); setTimeout(() => smartObj.interact(), 1000);
overhead: we create stand-in properties and their types for the members that will come from our mixins, that’s exactly what we want to avoid by using mixins
The pure function and delegation based Flight-Mixin Approach
'use strict'; // Implementation const EnumerableFirstLast = (function () { // function based module pattern. const first = function () { return this[0]; }, last = function () { return this[this.length - 1]; }; return function () { // function based Flight-Mixin mechanics ... this.first = first; // ... referring to ... this.last = last; // ... shared code. }; }()); // Application - explicit delegation: // applying [first] and [last] enumerable behavior onto [Array]'s [prototype]. EnumerableFirstLast.call(Array.prototype); // Now you can do: const a = [1, 2, 3]; a.first(); // 1 a.last(); // 3
# Generics
capturing the type
function identity<T>(arg: T): T { return arg; } let output = identity<string>("myString"); let output2 = identity("myString");
- when working with classes, static members can not use the class’s type parameter
Generic Constraints
interface Lengthwise { length: number; } function loggingIdentity<T extends Lengthwise>(arg: T): T { console.log(arg.length); // Now we know it has a .length property, so no more error return arg; }
- Type Parameters
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] { return names.map(n => o[n]); }
- Type Parameters
Using Class Types in Generics
function create<T>(c: {new(): T; }): T { return new c(); }
# Advanced Types
Intersection Types —
T & U
Union Types —
T | U
type Easing = "ease-in" | "ease-out" | "ease-in-out";
1 | 2
type guard — some expression that performs a runtime check that guarantees the type in some scope
type predicate type guards —
is
return typefunction isFish(pet: Fish | Bird): pet is Fish { return (<Fish>pet).swim !== undefined; } if (isFish(pet)) { pet.swim(); } else { pet.fly(); }
- called with some variable, TypeScript will narrow that variable to that specific type if the original type is compatible
typeof
type guards —if (typeof x === 'number')
instanceof
type guards
mapped types
type Readonly<T> = { readonly [P in keyof T]: T[P]; } type Partial<T> = { [P in keyof T]?: T[P]; } type Keys = 'option1' | 'option2'; type Flags = { [K in Keys]: boolean };
Partial
andReadonly
already in library- more in lib
conditional types —
T extends U ? X : Y
- when T is assignable to U the type is X, otherwise the type is Y
- predefined in
lib.d.ts
— access viaF12
Exclude<T, U>
– Exclude from T those types that are assignable to UExtract<T, U>
– Extract from T those types that are assignable to UNonNullable<T>
– Exclude null and undefined from TReturnType<T>
– Obtain the return type of a function typeInstanceType<T>
– Obtain the instance type of a constructor function type
tbc
# Modules
modules and scripts
- any file containing a top-level
import
orexport
is considered a module - a script whose contents are available in the global scope (and therefore to modules as well)
tsc --module commonjs Test.ts
for node.js
- any file containing a top-level
export
— ES6 likeimport
— ES6 likeIf a module identifier is only ever used as part of a type annotations and never as an expression, then no
require
call is emitted for that moduleImport a module for side-effects only
import 'module'
— module augmentation see belowimport types
// module.d.ts export declare class Pet { name: string; } // global-script.ts function adopt(p: import("./module").Pet) { console.log(`Adopting ${p.name}...`); }
also JavaScript
// a.js /** * @param p { import("./module").Pet } */ function walk(p) { console.log(`Walking ${p.name}...`); }
export =
andimport = require()
— model the traditional CommonJS and AMD workflow, such as- The
export = syntax
specifies a single object that is exported from the module - When exporting a module using
export =
, TypeScript-specificimport module = require("module")
must be used to import the module - Depending on the module target specified during compilation, the compiler will generate appropriate code for different target
- The
Ambient Modules
.d.ts
— Working with Other JavaScript LibrariesWe could define each module in its own .d.ts file with top-level export declarations
or write them as one larger
.d.ts
file. To do so, we use a construct similar to ambient namespaces, but we use themodule
keyword and the quoted name of the module which will be available to a later import/// <reference path="node.d.ts"/> import * as URL from "url";
direct import with
any
typedeclare module "hot-new-module"; import x, {y} from "hot-new-module";
Wildcard module declarations — non-JavaScript content
Module Resolution
# Namespaces
namespace
the
--outFile
flag to compile all of the input files into a single JavaScript output filenamespaces can span multiple files, and can be concatenated using
--outFile
Non-exported members are only visible in the original (un-merged) namespace
aliases —
import a =
namespace Shapes { export namespace Polygons { export class Triangle { } export class Square { } } } import polygons = Shapes.Polygons; let sq = new polygons.Square(); // Same as 'new Shapes.Polygons.Square()'
modules would be the recommended code organization mechanism
# Declaration
- Declaration merge — merges two separate declarations declared with the same name into a single definition
interfaces with same name will merge
namespaces with same name will merge, not the non-exported
Merging Namespaces with Classes, functions, enums — static field
classes can not merge with other classes or with variables
Module Augmentation —
declare module
// observable.ts stays the same export class Observable<T> { // ... implementation left as an exercise for the reader ... } // map.ts import { Observable } from "./observable"; declare module "./observable" { interface Observable<T> { map<U>(f: (x: T) => U): Observable<U>; } } Observable.prototype.map = function (f) { // ... another exercise for the reader } // consumer.ts import { Observable } from "./observable"; import "./map"; let o: Observable<number>; o.map(x => x.toFixed());
can’t declare new top-level declarations in the augmentation – just patches to existing declarations
Global augmentation
declare global
— add declarations to the global scope from inside a module- have the same behavior and limits as module augmentations
# JSX
basic usage
.tsx
file extension- enable
jsx
option
tbc
# Decorators
use
@f @g x // or @f @g x
- stage 2 proposal for JavaScript and are available as an experimental feature
- enable the
experimentalDecorators
compiler option
Decorator Factories — customize how a decorator is applied to a declaration
function color(value: string) { // this is the decorator factory return function (target) { // this is the decorator // do something with 'target' and 'value'... } } // call with @color('red')
Decorator Evaluation
- Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for — each instance member and each static member
- Parameter Decorators are applied for the constructor
- Class Decorators are applied for the class
Property Decorators
- cannot be used in any other ambient context
- The expression for the property decorator will be called as a function at runtime, with the following two arguments:
- Either the constructor function of the class for a static member, or the prototype of the class for an instance member
- The name of the member
sample decorators
function sealed(constructor: Function) { // @sealed Object.seal(constructor); Object.seal(constructor.prototype); } function enumerable(value: boolean) { // @enumerable(false) return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { descriptor.enumerable = value; }; } function configurable(value: boolean) { // @configurable(false) return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { descriptor.configurable = value; }; }
reflect-metadata
npm module — tbc
# Triple-Slash Directives
- Triple-Slash Directives
- single-line comments containing a single XML tag. The contents of the comment are used as compiler directives
- only valid at the top of their containing file
/// <reference path="..." />
— a declaration of dependency between files- instruct the compiler to include additional files in the compilation process
- also serve as a method to order the output when using
--out
or--outFile
- resolved in a depth first manner
--noResolve
— If the compiler flag--noResolve
is specified, triple-slash references are ignored- neither result in adding new files, nor change the order of the files provided
/// <reference types="..." />
— declares a dependency on a package- Use these directives only when you’re authoring a
d.ts
file by hand - an
import
for declaration packages - For example, including
/// <reference types="node" />
in a declaration file declares that this file uses names declared in@types/node/index.d.ts
- Use these directives only when you’re authoring a
/// <reference lib="..." />
— explicitly include an existing built-in lib file- Built-in lib files are referenced in the same fashion as the
"lib"
compiler option in tsconfig.json (e.g. uselib="es2015"
and notlib="lib.es2015.d.ts"
, etc.) - For example, adding
/// <reference lib="es2017.string" />
to one of the files in a compilation is equivalent to compiling with--lib es2017.string
- Built-in lib files are referenced in the same fashion as the
- more
# Type Checking JavaScript Files
- tbc
# Declaration Files
- tbc