- Published on
Intro to TypeScript
- Authors
- Name
- Curtis Warcup
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
interfaceto 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
?afterisActiveindicates 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
askeyword
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
iin front of the interface name
Functions
- specify the type of the
argumentsand thereturntype of a function
const sayHello = (name: string): string => {
return `Hello ${name}`
}
sayHelloaccepts astringas an argument and returns astring
Void
- if a function does not return anything, we must use the
voidtype
const noReturn = function (name: string): void {
console.log(name)
}
Promises
- a function that returns a
Promise(orasync) must wrap the return value in thePromisetype
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
Typeto simplyT
interface Container<T> {
contents: T
}
Compiling TypeScript tsconfig.json
we have seen we can call
tscto compile our TypeScript filescreate the
tsconfig.jsonfile
tsc --init
creates a
tsconfig.jsonfile 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
outFileis where the compiled JS file will go.- will create ONE file
- more common
outDiris 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