- Published on
Intro to TypeScript
- Authors
- Name
- K N Anantha nandanan
- @Ananthan2k
What is TypeScript?
- superset of JavaScript
- adds static types to JavaScript
- is compiled to JavaScript during the build process
- TS => typescript compiler => JS
- all JavaScript is valid TypeScript
Is very useful since it catches many errors before they are discovered at runtime.
- recall all the edge cases we had to test out in our previous projects.
TypeScript CLI
Install globally:
npm install -g typescript
tsc
CLI options
We can say...
tsc myFile.ts
And it will compile the file to JavaScript.
Now have access to tsc
command. (TypeScript Compiler)
# uses default settings; .js file will have the same name as .ts file
tsc index.ts # produces index.js file
# compile to named destination file
tsc index.ts --outFile my-index.js # produces my-index.js
# add the watch flag to avoid having to manually trigger the compiler
tsc index.ts --watch # watches for changes to index.ts
# the target flag allows us to specify the version of JS we are compiling to
tsc index.ts --target es6 # compiles to es6 syntax JS
tsc --init # will create a tsconfig.json file in the current directory
# after initializing, running `tsc` will use the configuration specified in the file
tsc
Primitive Types
How do we add types to something?
- use the colon
:
after a variable name to specify its type
const str: string = 'hello world'
const isLoggedIn: boolean = false
const num: number = 3.14
If we try to assign a value of the wrong type to a variable, TypeScript will throw an error.
let numTwo: number = 2
numTwo = 'hello' // error
// error TS2322: Type '"hello"' is not assignable to type 'number'.
- Assign multiple types to a variable using the
|
operator
let numThree: number | string = 3
numThree = 'hello' // valid
numThree = true // error
// error TS2322: Type 'false' is not assignable to type 'string | number'.
Arrays
- use the
[]
syntax to specify an array of a certain type - arrays are just collections of a type
- use the
Array<type>
syntax to specify an array of a certain type
const arr: number[] = [1, 2, 3]
const arrTwo: Array<number> = [1, 2, 3]
arr.push(4) // valid
arr.push('hello') // error
Objects and Interfaces
- are a bit more complex than arrays
- need to create an
interface
to specify the shape of the object
An interface
specifies the keys an object will have, and the type of the value associated with each key.
- we can put interfaces in a separate file and import them into other files
interface User {
name: string
age: number
isLoggedIn: boolean
}
const user: User = {
name: 'John',
age: 30,
isLoggedIn: true,
}
- this is like adding your own type definitions (this is known as a interface)
Another example:
interface Author {
name: string
penName: string
isActive?: boolean // optional params are marked with a question mark (?)
writeBook: (title: string) => boolean // function types need the type of the arguments and the return type
}
const curtis: Author = {} // error!
// error TS2741: Property 'name' is missing in type '{}' but required in type 'Author'.
const person: Author = {
name: 'Curtis',
penName: 'Curtis',
writeBook: (title: string) => true,
}
- the
?
afterisActive
indicates that the property is optional - when using an interface to define a function, the function type needs to be specified
- need to specify the type of the arguments and the return type
const agatha: Author = {} // error!
const stephen: Author = {} as Author // no error
Hack: To force TypeScript to accept a type, use the
as
keyword
Can create the interface a different way:
const user: { name: string; age: number } = {
name: 'Curtis',
age: 30,
}
Array of objects:
const users: User[] = []
users.push(user)
- it is common to place a
i
in front of the interface name
Functions
- specify the type of the
arguments
and thereturn
type of a function
const sayHello = (name: string): string => {
return `Hello ${name}`
}
sayHello
accepts astring
as an argument and returns astring
Void
- if a function does not return anything, we must use the
void
type
const noReturn = function (name: string): void {
console.log(name)
}
Promises
- a function that returns a
Promise
(orasync
) must wrap the return value in thePromise
type
const returnsPromise = (name: string): Promise<string> => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(name), 1000)
})
}
const returningPromise2 = (num: number): Promise<number> => {
return new Promise((res) => {
res(num * 2)
})
}
If you passed no arguments into the function, it would be an error.
returnsPromise() // error
Type Inference
- TypeScript can infer the type for us, then we don't need to specify it
- docs: Type Inference
No need to specify string
type:
const name = 'John'
Works with interfaces too:
interface User {
username: string
password: string
}
// logInUser accepts a User type
const logInUser = (user: User): boolean => {
return user.username === 'johnstamos'
}
// no type specified for potentialUser
const potentialUser = {
username: 'johnstamos',
password: 'pass123',
}
// no problem passing potentialUser to logInUser
const loggedIn = logInUser(potentialUser)
If we try to pass a user that doesn't have the correct properties, TypeScript will throw an error:
const potentialUser = {
username: 'johnstamos',
password: 'pass123',
age: 30, // error!
}
const isLoggedIn = logInUser(potentialUser)
// error!
// error TS2345: Argument of type '{ username: string; }' is not assignable to parameter of type 'User'.
// Property 'password' is missing in type '{ username: string; }' but required in type 'User'.
Methods
- same syntax as functions
interface IUser {
username: string
password: string
drinkLiquid: (fluid: string) => boolean
}
const curtis: IUser = {
username: 'curtis',
password: 'pass123',
drinkLiquid: (fluid: string) => {
return true // needs to return a boolean because of the return type in the interface
},
}
Callbacks
const higherOrderFunction = (callback: (num: number) => number): number => {
return callback(2)
}
Generics
- are interfaces when some or all types are not known at the time of definition time
- instead, they are passed in as arguments when the interface is used
- allow us to pass a variable to a type
- we specify the variable using
<
and>
interface Container<Type> {
title: string
contents: Type
}
const numContainer: Container<number> = {
title: 'number container',
contents: 7,
}
const stringContainer: Container<string> = {
title: 'string container',
contents: 'hello',
}
- It is common to shorten
Type
to simplyT
interface Container<T> {
contents: T
}
Compiling TypeScript tsconfig.json
-
we have seen we can call
tsc
to compile our TypeScript files -
create the
tsconfig.json
file
tsc --init
creates a
tsconfig.json
file in the current directory. You should do this in the root file.
- modules
- want to preserve import/export syntax use
es6
- want to preserve import/export syntax use
- root directory
- may make it the "./src" directory
- output directory
- may make it the "dist" directory
- allow JS files
- set to
true
- need to turn this on to allow JS files to be compiled
- set to
- emit
- where does it go to?
- IMPORTANT
outFile
is where the compiled JS file will go.- will create ONE file
- more common
outDir
is where the compiled JS file will go.- will create a directory with the compiled JS file
TSC --watch
watch for changes in the file and recompile
tsc --watch
Will go on in the background. Even in react, you can use this to compile your TS files.
Useful Resources
- TS site
- TS Compiler Options
- TS in 5 minutes
- TS Playground: TS Playground