It’s unclear what features were introduced with this release, or if it was just an assurance of stability.
This release offers no new features, but improves compilation speed by around 4x.
TODO
var bounds: [number, number, number, number] = [10, 10, 100, 100];
var top_left = new Point(bounds[0], bounds[1]);
Generic tuple types are awkward. Even with type aliases from 1.4, the following is invalid:
type MatchResult<T> = [RegExp, T] // Syntax Error!
Instead, we must use the bulkier interface syntax:
export interface MatchResult<T> extends Array<RegExp | T> {
0: RegExp;
1: T;
}
function join(text: string | string[]) {
return Array.isArray(text) ? text.join('') : text;
}
Use typeof
and instanceof
to type-check at runtime, and the TypeScript compiler will (should) infer the more specific type for the duration of the check’s scope.
function add1(arg: number | string) {
if (typeof arg === "number") {
return arg + 1;
}
else {
return arg + 'one';
}
}
This works only on union types, not on a type hierarchy, but you can specify a class and its subclasses as a union type when needed.
type ErrorCallback = (error?: Error) => void;
Unlike normal enums, Position
below will not appear in the Javascript output.
const enum Position { Outside, Beginning, Inside };
var current_position = Position.Outside;
function main() {
for (let index = 0; index < 10; x++) {
console.log(index.toFixed());
}
for (let index = 'a'; index.length < 10; index += '!') {
console.log(index);
}
}
var point = { x: 10, y: 240 };
console.log(`Drawing point at (${point.x}, ${point.y})`);
import * as util from "util-enhanced";
import { format, inspect } from "util";
Suppose ndarray.ts
has an export default class NDArray { ... }
.
We could import the NDArray
class with import NDArray from "ndarray"
while allowing other exports to be imported by wildcard or by name.
var [left, right] = pair.split('=');
In function parameters:
function move({x, y}, dx: number, dy: number) {
return {x: x + dx, y: y + dy};
}
var originalPoint = {x: 10, y: 10};
var movedPoint = move(originalPoint, 30, 40);
console.log('current location:', movedPoint);
// outputs: "movedPoint == {x: 40, y: 50}"
It will also destructure classes:
class Point {
constructor(public x: number, public y: number) { }
}
var pointInstance = new Point(-5, -5);
console.log('current location:', move(pointInstance, 10, 10));
// outputs: "current location: { x: 5, y: 5 }"
Decorators are defined as functions and can be applied, with @myDecorator
or @myDecorator('message')
syntax, to class
definitions, properties within class
definitions, functions, or function parameters.
Depending on the target, the decorator function should implement one of these signatures:
<TFunction extends Function>(target: TFunction) => TFunction | void
(target: Object, propertyKey: string | symbol) => void
<T>(target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void
(target: Object, propertyKey: string | symbol, parameterIndex: number) => void
Memoized getter. Definition:
function memoize<T>(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<T>) {
var get = descriptor.get;
var memoizedPropertyKey = `_memoized_${propertyKey}`;
descriptor.get = function() {
if (!this.hasOwnProperty(memoizedPropertyKey)) {
this[memoizedPropertyKey] = get.call(this);
}
return this[memoizedPropertyKey];
}
return descriptor;
}
Application:
class NumberSeries {
constructor(public value: number) { }
@memoize
get numberOfDigits(): number {
console.log('Getting the total number of digits in all natural numbers < %d', this.value);
var digits = '';
for (var i = 0; i < this.value; i++) {
digits += i.toFixed(0);
}
return digits.length;
}
}
Execution:
var million = new NumberSeries(1000000);
console.error(million.numberOfDigits);
// immediately outputs: 'Getting the total number of digits in all natural numbers < 1000000'
// short pause, then outputs: '5888890'
console.error(million.numberOfDigits);
// immediately outputs: '5888890'
References:
Requires the “reflect-metadata” package, npm install reflect-metadata
, which provides type declarations in a file named reflect-metadata.d.ts
in its root directory.
/// <reference path="../node_modules/reflect-metadata/reflect-metadata.d.ts" />
Importing it exposes a new global variable, Reflect
.
import 'reflect-metadata';
Metadata is defined via the Reflect.defineMetadata(metadataKey: any, metadataValue: any, target: Object)
method.
Create a simple class decorator to facilitate the metadata definition:
function ClassPriority(priority: number) {
return function(target: Object) {
Reflect.defineMetadata('priority', priority, target);
}
}
Apply it to a couple of types:
@ClassPriority(1)
class StringValidator {
// ...
}
@ClassPriority(2)
class NumberValidator {
// ...
}
Instantiate one like so:
var validator = new NumberValidator(/* ... */);
And inspect its class metadata via Reflect.getMetadata(metadataKey: any, target: Object): any
:
console.log('validator priority:', Reflect.getMetadata('priority', validator.constructor));
// outputs: 'validator priority: 2'
Replace the extension .ts
with .tsx
and you can use JSX markup directly, as in a .jsx
file with Babel, which compiles to React.createComponent(...)
or React.createElement(...)
calls.
Classes can be declared anonymously and used immediately.
I don’t yet know where this would be useful.
Replace class
with abstract class
to keep it from being instantiated directly.
Otherwise, it is inheritable and extensible as usual.
Prefix class methods with abstract
to require that subclasses implement it.
So, it’s kind of like an interface with defaults.
C: A & B
is like saying interface C extends A, B { ... }
, but more concise, doesn’t require declaring a distinct new type, and works for generics.
If typeof
and instanceof
aren’t cutting it, you can declare a function that returns a type assertion like function(id: string): id is GUID { ... }
and then when you use that function in a conditional, id
will have the type GUID
for the scope of that conditional.
Before, you would have had to declare a new variable with a <GUID><any>id
type coercion inside that conditional if you wanted to benefit from your type check.
Interfaces now defines an object’s maximal properties. Before, it only described the necessary properties. E.g.:
interface Person { first: string; last: string }
Before, this would be fine:
var current_user: Person = { first: 'Chris', last: 'Brown', age: 39 }
Now, that will cause a type error.
When using --module commonjs
output, the TypeScript compiler will look for type declarations in a variety of places:
1. Check in node_modules for <module name>.d.ts
2. Search node_modules\<module name>\package.json for a typings field
3. Look for node_modules\<module name>\index.d.ts
4. Then we go one level higher and repeat the process
I think this is the most promising of the TypeScript 1.6 features/improvements in making tooling better.
Type aliases can now have type parameters. Pretty basic stuff, I’m not sure why this wasn’t in the original type aliasing implementation.
Use async function myFunc(...) {...}
instead of function myFunc(...) {...}
to declare an asynchronous function.
myFunc
returns a Promise, that will be used as the await
-able or then
-able value.myFunc
doesn’t return a Promise, its return value (or the implicit return undefined
at the end of the function, if it does not return anything) will be implicitly wrapped in a Promise.
This is useful if myFunc
itself contains some await
calls, which will delay the final return value.this
Most useful for method chaining. If an interface or class has a this
type, any subclasses or implementations will effectively replace that this
type with their own type.
For example, EventEmitter
can be declared as:
declare module 'events' {
export class EventEmitter {
on(event: string, listener: Function): this;
/* ...all the other methods... */
}
And I can inherit it like so:
import {EventEmitter} from 'events';
class SelfClosingElementParser extends EventEmitter {
readString(xmlString: string): this {
xmlString.match(/<(.+) />/g, match => {
this.emit('data', match[1]);
});
}
}
And chain calls like this:
new SelfClosingElementParser()
.on('data', tag => console.log(tag)) // <-- this was fine prior to 1.7
.readString(rawString) // <-- but this would have raised an type error,
// since .on() would have returned an EventEmitter,
// not the SelfClosingElementParser.
Useful if you want to compile to ES6 except for its module system (which Node.js v4 doesn’t support), specifying a different module system, like “commonjs”.
**
operator**
is the new Math.pow
as of ES7.
I.e., base ** exponent === Math.pow(base, exponent)
.
“Constant signatures” can be used to provide types for events:
export interface ImapFetch extends NodeJS.EventEmitter {
on(event: string, listener: Function): this;
on(event: 'message', listener: (message: ImapMessage) => void): this;
on(event: 'error', listener: (error: Error) => void): this;
}
Recommended .gitignore
(assuming all JavaScript files are generated from TypeScript):
*.js
*.d.ts
Recommended .npmignore
:
*.ts
!*.d.ts
tsconfig.json
Extra .npmignore
entries for packages using Travis CI:
.travis.yml
tests/
@bind
(decorator)Add this to your tsconfig.json
:
{
"compilerOptions": {
"experimentalDecorators": true,
...
},
...
}
Then, with the following definition of bind
in scope:
function bind<T extends Function>(target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void {
return {
configurable: true,
get(this: T): T {
const value = descriptor.value.bind(this)
Object.defineProperty(this, propertyKey, {
value,
configurable: true,
writable: true,
})
return value
},
}
}
You can use it like this:
class NumberInput extends Component {
@bind
onChange(ev: FormEvent<HTMLInputElement>) {
const input = ev.currentTarget
const value = parseInt(input.value, 10)
this.setState({value})
}
render() {
const {label = 'Pick a number'} = this.props
const {value = 0} = this.state
return (
<label>
<div><b>{label}</b></div>
<input type="number" value={value} onChange={this.onChange} />
</label>
)
}
}