Iterators and Generators
Iterators are basically objects that can be hold more than one item. Iterators are collections of objects or variables or items that can be iterated on to do some sort of processing on each item in the collection. Arrays are an exmaple of iterators.
In TypeScript, an object is demmed iterable if it has an implementation for Symbol.iterator
property. Some built-in types like Array, Map, Set, String, Int32Array, etc. have their Symbol.iterator
property already implemented.
Symbol.iterator
function on an object is responsible for returning the list of values to iterate on.
for...of...
statements
for...of...
loops over an iterable object will invoke the Symbol.iterable
property on the object.
1 | let someArray = [1, 'string', false] |
Intersection Types
An intersection type combines multiple types into one. This is allows you to add together existing types to get a single type that has all the features you need.
Union Types
Union Types are closely related to intersection types, but they are used very differently. Occasionally, you’ll run into a library that expects a parameter to be either a number
or a string
.
Type Guards
Union Types are useful for modeling situations when values can overlap in the types they can take on.
TypeScript has what is called a type guard
.
A type guard is some expression that performs a runtime check that guarantees the type in some scope. To define a type guard, we simply need to define a function whose return type is a type predicate
.
1 | function isFish(pet: Fish | Bird): pet is Fish { |
pet is Fish
is our type predicate in this example. A predicate takes the form parameterName is Type
, where parameterName
must be the name of a parameter from the current function signature.
Any time isFish
is called with some variable, TypeScript will narrow
that variable to that specific type if the original type is compatible.
1 | if (isFish(pet)) { |
Notice that TypeScript not only knows that pet is Fish
in the if branch, it also knows that in the else branch you don’t have a fish, so you must have a Bird
.
typeof
type guards
1 | function isNumber(x: any): x is number { |
However, having to define a functino to figure out if a typeis a primitive is kind of a pain.
TypeScript will recognize typeof x === 'number'
as a type guard on its own. This means we could just write checks inline.
1 | function padLeft(value: string, padding: number | string) { |
These typeof
type guards are recognized in two different forms: typeof v === 'typename'
and typeof v !== 'typename'
.
instanceof
type guards
instanceof
type guards are a way of narrowing types using their constructor function.
The right side of the instanceof
needs to be a constructor function, and TypeScript narrow down to:
the type of the function’s
prototype
property if its type is not any.the union of types returned by that type’s construct signatures.
in that order.
Generics
1 | function identity<T>(arg: T): T { |
We add a type variable T
to the identity function. This T allows us to capture the type the user of the code provides, so that we can use that information later.
We say that this version of the identity
function is generic, as it works over a range of types.
Once we’ve written the generic identity function, we can call it in one of two ways.
The first way is to pass all of the arguments, including the type argument, to the function.
1 | let output = identity < string > 'myString' // type of output will be string |
Here we explicitly set T to be string
as one of the arguments to the function call, denoted using <>
around the arguments rather than ().
The second way is also perphaps the most common. Here we use type argument inference – that is, we want the compiler to set the value of T for us automatically based on the type of the argument we pass in.
1 | let output = identity('myString') // type of output will be 'string' |
Working with Generic Type Variables
When you begin to use generics, you’ll notice that when you create generic functinos like identity
, the compiler will enfore that you use any generically typed parameters in the body of the function correctly.
Generic Types
1 | function identity<T>(arg: T): T { |
Generic Classes
A generic class has a similar shape to a generic interface.
Generic classes have a generic type parameter list in angle brackets(<>) following the name of the class.
1 | class GenericNumber<T> { |
Generic classes are only generic over their instance side rather than their static side.
Decorator
A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter.
Sometimes it is required to have additional features to support annotating or modifying classes and class members. Decorators provide a way to add both annotation and a meta-programming syntax for class declarations and members.
Decoration use the form @expression
, where expression
must evaluate to a function that will be called at runtime with information about the decorated declaration.