Web Front-end Notes - Typescript
- Types
- Classes & Interfaces
- Type Guards
- Index Properties
- Function Overloads
- Optional Chaining
- Generic Types
- Decorators
- Typescript Compiler
Types
typeof returns the type value in string.
Core Types
The core primitive types in TypeScript are all lowercase!
number: numbers are by default floats in both JS and TS,+inputis equal toparseFloat(input)stringbooleanobject: object types are written almost like objects, but with key/type pairs.Array:Tuple(introduced in TS):[number, string]is a tuple type definition.pushmethod is allowed for Tuple.Enum(introduced in TS):enum { NEW, OLD }, default is number type starting with 0 and automatically increment.Any(introduced in TS): takes away all the benefits that typescript gives you. Used as fallback.
When assigned
nullto variables of primitive types, the Typescript compiler will not complain.
Object Type
1 | // typescript type inference for object type: |
Union & Intersection Types
Union types are types that accept more than 1 type:
1 | let input1: number | string; |
Intersection types allow us to combine types.
1 | type Admin = { |
- In the case of union types, intersection types output the members in common
- In the case of object types, intersection types output the combination.
Literal Types
Literal types are based on core types, like string or number, but with poticular value of those types, and often used together with union types:
1 | let resultConversion: "as-number" | "as-text"; |
Discriminated Unions
Build discriminated union by giving every interface an extra property(whatever name you want, typically “kind” or “type”),
1 | interface Bird { |
Type Aliases
type keyword in Typescript is used to set type aliases:
1 | // use with union types |
Void Type & Never Type
undefined is not valid return type for functions in Typescript, because Typescript would expect the function to have a return statement, just not return a value. void means the function does not return anything, it is allowed that the function does not have a return statement.
never type can be used as another return type for functions that never return anything, essentially throw errors or in a infinite loop.
Functions as Types
Function type allows to assign a function signature to variables, which is quite simular to delegates in C#.
1 | // define a function type that takes 2 number type parameters and return a number |
When function type is used with a callback function, the
voidmeans the function will not use the returned value, it does not restrict that the function cannot have areturnstatement.
Unknown Type
Any is the most flexible type in Typescript which basically disables all type checking. unknown is a bit more restrictive than Any type, Typescript will have type checkings on it before assign it to another variable with clear type:
1 | let userInput: unknown; |
Classes & Interfaces
Classes
Class exists since ES6, typescript supports it and extends a few use cases.
- Consturctor:
constructoris the syntax sugar which is called when you call thenew SimpleClass()to create class instance. thiskeyword: refers to the concrete instance of the class in methods. However,thistypically refers to the object that is responsible to call methods. We can declarethis:Departmentin method parameters, so that Typescript ensures thatthiskeyword inside a method should always refer to an instance of typeDepartment.publicis the default modifer for class members.- Shorthand Initialization:
constructor(private id: string, private name: string){};: declare class fields as constructor parameters. readonlymodifier: it makes sure the field can be only written during initialization.extendskeyword allows class to inherit from another class. One class can only inherit from one parent class.- Always call
super()first in the inherited class’s own constructors. - With
es7, we can directly use property definition in class, no need to callsuper.
- Always call
- Override:
- Define the same method signature in derived class to override the implementation of that method in base class.
- use
protectedkeyword to allow derived classes to have access.
- Getters & Setters:
getkeyword is used to create a getter. Getters are defined in method signature, while it is accessed like properties. Getter is by default public.setkeyword is used to create a setter, but it takes avalueargument.
- Abstract Class:
abstractkeyword can only be used in abstract class to define abstract members.
Interfaces
interfaceonly exists in Typescript- Interfaces define a structure of objects.
- Typically used as a custom type
- A class can implement an interface by using
implementskeyword, while it can not implement a custom type in the early days due to historical reasons. - Interface can also be used to define a function type:
interface AddFn { (a: number, b: number): number; }, but it is more common to use custom type to define function types. - Specify optional properties/parameters/methods by adding
?after the property name. Default value of optional properties isundefined. - Interface is purely a development feature for writing better codes.
Type Alias vs. Interfaces
- Interfaces are basically a way to describe data shapes, for example, an object.
- Type is a definition of a type of data, for example, a union, primitive, intersection, tuple, or any other type.
Declaration Merging
One thing that’s possible to do with interfaces but are not with types is declaration merging.
1 | interface Song { |
Extends and Implements
In TypeScript, we can easily extend and implement interfaces. This is not possible with types though.
What should I use?
- Interfaces are better when you need to define a new object or method of an object.
- Types are better when you need to create function types.
Type Guards
“typeof” keyword
typeof is used to check primitive types. However, using typeof to check custom types always return Object.
“in” keyword
Instead, we use in keyword for type guards of custom types.
1 | // The way to check if a property exsits in an object at runtime. |
“instanceof” keyword
Another type guard method is to use instanceof keyword:
1 | type Vehicle = Car | Truck; |
instanceofcan be used forclass. However,instanceofcan not be used to checkinterface, because interfaces are not translated into any javascript code.
Type Casting
Typescript has no idea what type of the element is:
1 | const element = document.getElementById("user-input"); |
before the variable
1 | const element = <HTMLInputElement>document.getElementById("user-input"); |
“as” keyword after the variable
1 | const element = document.getElementById("user-input") as HTMLInputElement; |
Index Properties
Index types allow us to create objects which are more flexible regarding the properties they might hold.
1 | interface ErrorContainer { |
Every property added the key must be a string and the value must be a string.
Function Overloads
To define function overloads:
- Same function name
- Different parameter amount, types and return type.
Optional Chaining
In Javascript. we often do something like fetchedUserData.job && fetchedUserData.job.title to securely access job.title in case it does exist. In typescript, we can use fetchedUserData.job?.title as the equivalent.
Nullish Coalescing
const storeData = userInput ?? 'DEFAULT';: means only return “DEFAULT” only when userInput is null or undefined.
Generic Types
1 | function merge<T, U>(objA: T, objB: B); |
Constraints
Use extends to set constraints for type arguments.
1 | function merge<T extends object, U extends object>(objA: T, objB: B); |
“keyof” Constraint
Type argument U should be one of existing key of type T:
1 | function extractAndConvert<T extends object, U extends keyof T>(); |
Typescript built-in generic utility types
These types only exsits in typescript world.
Partial<T>Readonly<T>- …
Decorators
- Decorators execute when defining classes. It does not execute at runtime.
- Use
@to use the decorators - Decorator factories runs in top-bottom order, while decorators run in bottom-top order.
Class Decorators
- Class decorators are functions with one argument of target constructor.
- Class decorators can return a new class to extend the original class with extra logic.
Property Decorators
Property decorator receives 2 arguments:
- target: prototype of the object if attached to an instance property, or the constructor function if attached to a static property
- property name: property name of
stringorsymboltype.
Accessor Decorators
Accessor decorator receives 3 arguments:
- target: prototype of the object if attached to an instance accessor, or the constructor function if attached to a static accessor
- name: name of the accessor itself of type
stringorsymbol. - property descriptor: of Typescript built-in type
PropertyDescriptor.
Methods Decorators
Method decorator receives 3 arguments:
- target: prototype of the object if attached to an instance method, or the constructor function if attached to a static method
- name: name of the method of type
stringorsymbol. - property descriptor: of Typescript built-in type
PropertyDescriptor.
Accessor and methods decorators can return a new PropertyDescriptor to replace the original one.
Parameter Decorators
Parameter decorator receives 3 arguments:
- target: prototype of the object if attached to an instance method, or the constructor function if attached to a static method
- name: name of the method in which the parameter is used, of type
stringorsymbol. - position of the parameter: of type
number, index starting with 0.
Typescript Compiler
Watch mode:
Use tsc app.ts --watch/-w to watch changes for a single file.
Compile entire project:
tsc --init creates a tsconfig.json file for the working directory and considers it as a typescript project, run tsc will compile all the files under the folder/sub-folders.
tsconfig.json
tsconfig.json tells typescript how the compiler behaves.
"exclude": ["node_modules"]: accept an array of files and folders to be excluded. It accepts wildcard pattern.node_modulesis excluded by default."include": []: accept an array of files and folders to be included. If it is set, everything not covered byincludewill not get compiled."files": []: allows to point the individual files of specific paths.
compilerOptions section
"target": "es5": tells typescript in which ES version you want to compile the code to."es5"is the default value."lib": ["dom", "es6", "dom.iterable", "scripthost"]: allows to specify which default objects and features typescript knows. Typescript does not know anything aboutDOMobjects by default. However the default value depends on thetargetoption. For example, fores6, it by default includes all the features that are globally available in ES6, and it assumes that all DOM APIs are available. Basically the default libs allow your compiled javascript run in the browser."allowJs": false: allow to include javascript files in the complaination"checkJs": false: will not compile javascript files but will check syntax and report errors."sourceMap": false: helps with debugging. If set totrue, the compiler will also generate.js.mapfile as well, which acts as a bridge between javascript and typescript. The browser will also load the typescript file and allow debugging."outDir": "./": tell the typescript compiler where the created javascript files should be stored."rootDir": "./": make sure typescript does not look into others folders."removeComments": false: will remove any comments after compilation."noEmit": false: does not generate any javascript files, like dry run."noEmitOnError": false: if set totrue, typescript will stop generating any outputs if any error detected."strict": false: If set totrue, it enables all following strict type-checking options:"noImplictAny": false: If set totrue, it ensures that we have to be clear about the types of the parameters that in no way can be inferred."strictNullChecks": false: If set tofalse, the compiler will not complain a potential null value variable."strictFunctionTypes"::"strictBindCallApply":: If set totrue, it checks if it makes sense(match arguments) of the arguments you passed tobind,callandapplyfunctions."strictPropertyInitialization"::"noImplicitThis"::"alwaysStrict":: If set totrue, it controls that the generated javascript code always use “strict” mode.
"noUnusedLocals": false: If set totrue, the compiler will give warnings on unused local(like within functions) variables, stops compiling."noUnusedParameters": false: If set totrue, the compiler will give warnings on unused function parameters, stops compiling."noImplicitReturns": false: If set totrue, every path of a function is forced to have areturnstatement, otherwise it will be considered as errors.
Debugging with Visual Studio Code
- Install
Debugger for Chromeextension in VS Code. - Enable
"sourceMap": trueoption intsconfig.tsfile. - In your project, click debug and select “Chrome” as the environment.
- In
.vscode/launch.jsonfile, make sure theurlvalue points to the right local url for you local web server. - Set some break points, and click Start Debugging.
Declaration Files
Use declare module to define type definitions for packages that do not come with built-in types. See details here.