TS
TS is superset(backwards compatible) of JS, created to build large scale projects with type safety, backwards compatibility and additional features(it is common for TS to implement some TC39 non-released proposals)
- in it’s core, TS is type liner for JS with some additional stuff, that is added via compilation step
- types - added via annotations or automatically and throw compile-time errors
- or not, just use as any ;)
- note, TS is based on inferred types, so you don’t always have to add types by hand, but sometimes it can be useful/necessary
- TS uses structural type compatibility checks, meaning two types will be treated the same, if they have identical fields(despite the order)
- syntax - improved classes, interfaces,
Set
etc - tooling - IDE integrations, compilers etc
- types - added via annotations or automatically and throw compile-time errors
Compilation & Usage
Compilation(with the fact, that TS is superset) makes possible to use TS code as JS
- worse to mention, that you can also use JS in TS context by itself or(common practice) with
d.ts
(type definition) files, that describe types of exported function(similar to JSDoc)
usage
- install
typescript
package as dev-dependency - add
tsconfig.json
, file for compile configurationtarget
- version of JS to compile tomodule
- module system to usestrict
- strict type checkingoutDir
- output for compiled filesrootDir
- dir with TS files to compileinclude
- array with additional dir patterns to compileexclude
- array with additional dir patterns to avoid compiling
- compile project/individual files via
tsc
, command line compilation tooltsc
can also take options fromtsconfig
via--
parameters
there is alternative to tsc
for Node.js - ts-node
, which is hooking into node’s APIs and enabling direct TS execution without compilation step
also there is possibility to run TS live in browser via ts-playground
Types
Primitive Types
- boolean -
true
/false
value - number - float or int values
- string - UTF-16 character sets
- void - usually inferred type, that signifies that function don’t have explicit
return
OR returns nothing(return;
) - undefined + null - corresponding type for JS’s
undefined
+null
- note: TS allows to avoid nullish values check by disabling
strictNullChecks
, but it is usually not worst it
- note: TS allows to avoid nullish values check by disabling
- symbol - corresponding type for JS’s
Symbol
Object Types
- interface - used specify a format/type of an object, commonly used as contract, that specifies a structure of a class/object
- can be used as abstract class, that other classes are
implement
- unlike
type
interface’s purpose is more about defining an object structure and to work with classes(they themselves can be extended etc), buttype
is more about aliasing other type or some kind of type combination- extending is done via
extend
, which states that interface must have all properties of it’s “parent”, but can have some more
- extending is done via
- can be used as abstract class, that other classes are
- classes - similar to JS’s classes, but with
- syntactic sugar
- you can specify params of
constructor
and TS will auto init them
- you can specify params of
- property access modifiers:
private
,protected
,public
for in-class, class+children or public method access
- syntactic sugar
- enum - object-like numeric or string-base type, that commonly used to create set of cases
- can be used as runtime value, caze compiled into real JS object
- if no value provided to enum key it will be numeric(
prev
+ 1)- if no
prev
it set to0
- if no
- array - used to specify type of an array
- can be done as
Array<T>
orT[]
- can be done as
- tuple - array-like structure, but we type it’s exact size and what type of each element in it
- example:
type Tuple = [number, string];
- example:
Other
- any - basically states that value can be anything, so we can access any methods, call it as function etc, without any type error
- generally unrecommended to use
- object - similar to interface, but used specifically with objects
- example:
type Obj = {a: number, b: boolean};
- example:
- unknown - safer version of any
- we can assign any value to
unknown
, but we can use unknown value like some type, before adding some type-checking
- we can assign any value to
- never - represent some value, that can be never achieved
- examples
- result of a function that always throws
- type after impossible type-narrowing
- we can assignee
never
to every type, but not backwards(exceptnever
itself)
- examples
Assertion
Assertion is a way to state to TS, that value must be treated differently and not what it inferred to be
syntax:
<T>value
value as T
- more common
variations:
as const
- compiler should treat value as read-onlyas T
- override inferred type to something else- usually a way to tell a compiler additional info about value, that compiler don’t know
- ok to use, but not the best practice
value!
- non nullish assertion, tells compiler that value can’t benull | undefined
- can lead to runtime errors and down-time in production, so be careful :)
satisfies
- used to ensure that expression is matched with type, but make it not so strict as interface- useful when we need to make object with keys from enum(or similar type) and some type of value
Type Combination & Manipulation
- union - combine multiple types into one, that represents all of them
number | string
(it is also called a Hybrid Type, meaning variable can be some of those types)
- intersection - create new type from combination of properties of other types, with saving all properties
ObjType1 & ObjType2
- type alias - possibility to create alias for other type
- common example:
type A = {a: number};
- interesting example:
type Number = string;
- common example:
keyof
- used to extract object keys as union typetype AKeys = keyof A; // "a"
Type Guards & Narrowing
Technic to narrow down a type(for example union to just one of the types in it) in order to do some specific actions with it
typeof
- default JS way to get string name for variable type- commonly used for primitive types
instanceof
- default JS way to check if some object is prototype-instance of a Class===
- can also auto-narrow type for TS- not commonly used, but may be helpful
const isNum = (v: unknown): v is number
- such function is called type predicate, it returnsboolean
, which determents value type- in modern TS it is rare to meet this type of a functions, but earlier they can be met in filters to guarantee that we filter out some type from array
notes:
- TS don’t expect values in
if
,||
,!!
etc use-cases to beboolean
Functions
TS functions are same as JS, but with possibility add add parameter, return types(can be generic)
Interestingly, you can declare two+ functions with same name, but different parameters(overload functions) and TS will determine which one to execute, based on number, type or order of parameters, like this:
It is useful pattern when migrating from JS to TS, but it general better keep one function <-> one use-case(interface) and not overcomplicate function
Classes
Similar to JS, but with some extra syntactic sugar and types
One of main features is constructor params with access features, this allows not only state access restrictions to a property, but auto-init this property Consider classical approach and TS one:
We can also do constructor overloading, similar to function one, by specifying several types and one implementation, that covers all cases
Access modifies:
- public (default) - property can be accessed from everywhere
- private - property can be accessed only from inside of a class
- protected - property can be accessed only from inside of a class or it’s subclass
Abstract classes - class, that acts as blueprint for other classes, that can’t be instantiated and only can be extended by subclass
- can consist of abstract methods without implementation, that must be overridden and implemented
We can do Polymorphism and Inheritance in TS via method overriding and extends
- unlike in JS, when overriding we must keep signature(parameters and result type) exactly the same
TS allows to use decorators in classes(property, method and parameter) in a way of adding additional functionality
- useful for logging, validation, optimization etc
Generics
Generic is standard pattern, that allow functions to work with different parameter types, where type will be specified similar to placeholder
Key note, that TS have mechanisms to infer generics, so good written generic function can avoid explicit type annotation at all
We can use generics with: function, class, interface, object type
It is possible to constrain generic via <T extends Type>
syntax, which states to TS, that T
must implement Type
Advanced Types
Set of type constructs that enables more advanced typings
commonly used:
- mapped types - new type is generated by mapping keys of existing type
- example:
readonly [Key in keyof TObj]: TObj[Key]; // make all keys readonly
- example:
- conditional types - new type is selected via boolean condition of input types
- example:
T extends U ? T : U
- example:
T extends (...args: any[]) => infer R ? R : any;
- this code is extracting return type of a function, by using
infer
keyword, that creates type variable, that can be referenced in true branch ofextends
condition
- this code is extracting return type of a function, by using
- example:
- literal types - literal value set as type, meaning we can set a specific number, string or boolean value as type
- TS will check not only for type, but also for value to be same
- example:
type Num = 37;
- template literal types - way to manipulate string templates by injecting types into them
- example:
- recursive types - way to define type, that references itself
- common for data structures like linked lists or trees
Utility Types
Partial<TObj>
- make all object properties optionalPick<TObj, strLiteral | strLiteral2 ...>
- take specified properties from objectOmit<TObj, strLiteral | strLiteral2 ...>
- take all properties from object and remove specified onesReadonly<TObj>
- make all properties readonlyRecord<TKey, TVal>
- make object type with specified key, value types- commonly used with
satisfies
and type mapping
- commonly used with
Exclude<type1 | type2 ..., type3 | type4 ...>
- exclude specified types from union typeExtract<type1 | type2 ..., type3 | type4 ...>
- take specified types from union typeNonNullable<T>
- construct type by excludingnull | undefined
Parameters<TFunc>
- construct tuple type from types of function parametersReturnType<TFunc>
- gives type, that function will returnInstanceType<typeof TClass>
- gives class constructor function type typeAwaited<Promise<T>>
- unwraps(recursively) promise type and returns it
THIS
In TS we can explicitly set interface of this
Can be done:
- automatically if function is an obj method
- declaratively, by acknowledging fact that JS restricts parameter with name this, in such way:
Other
Modules - used for code organisation and reusability
- internal(namespace) - used in a file scope
- example
- external - used in a project scope
- external modules can be compiled into JS’s CommonJS or ES modules
- example:
- .
- sub-variation of external module is possibility to include code into one file in compilation process via
/// <reference path="File.ts" />
, which will inject code fromFile.ts
into current file- referencing is used in combination with namespaces
- sub-variation of external module is possibility to include code into one file in compilation process via
- ambient - type of modules that provide types for non TS modules(via
d.ts
files)- example:
Augmentation - way to add declarations to some scope(namespace or global)
- main use-case is to type safely extend
window
,global
etc - global augmentation example:
Ecosystem:
- Prettier - formatting
- ESLint - linting
- Build tools:
- use-cases: spinning-up servers, compiling/transpiling/minifying/etc code, serving files, linting, pre-commit checking etc
- vite - most popular of them
- Other popular packages:
- zod - real-time data validation lib
- ts-morph - manipulation with TS code
- ts-node - direct TS execution in node env
- ts-jest - TS enabler for Jest(testing lib)