TypeScript is a typed superset of JavaScript that is compiled to JavaScript, which means TypeScript is JavaScript with some additional features. TypeScript was designed by Anders Hejlsberg (the designer of C#) at Microsoft and is open source.
Let's see what the features of TypeScript are and how to convert JavaScript to TypeScript.
TypeScript features
This section will try to summarize the most important features you should be taking advantage of:
- TypeScript is JavaScript: Any JavaScript code you write will work with TypeScript, which means if you already know how to use JavaScript basically you have all you need to do TypeScript; you just need to learn how to add types to your code. All the TypeScript code is transformed into JavaScript at the end.
- JavaScript is TypeScript: This just means that you can rename any valid .js file with the .ts extension, and it will work.
- Error checking: TypeScript compiles the code and checks for errors, which helps a lot to highlight errors before we run our code.
- Strong typing: By default, JavaScript is not strongly typed. With TypeScript, you can add types to all your variables and functions, and you can even specify the returned value types.
- Object-oriented programming supported: It supports concepts such as classes, interfaces, inheritance, and so on.
Converting JavaScript code into TypeScript
In this section, we will see how to transform some JavaScript code into TypeScript.
Let's suppose we have to check whether a word is a palindrome. The JavaScript code for this algorithm will be as follows:
function isPalindrome(word) {
const lowerCaseWord = word.toLowerCase()
const reversedWord = lowerCaseWord.split('').reverse().join('')
return lowerCaseWord === reversedWord
}
You can name this file palindrome.ts.
As you can see, we are receiving a string variable (word), and we are returning a boolean value, so how will this be translated to TypeScript?
function isPalindrome(word: string): boolean {
const lowerCaseWord = word.toLowerCase()
const reversedWord = lowerCaseWord.split('').reverse().join('')
return lowerCaseWord === reversedWord
}
You're probably thinking great, I just specified the string type as word and boolean type to the function returned value, but now what?
If you try to run the function with some value that is different from a string, you will get a TypeScript error:
console.log(isPalindrome('Level')) // true
console.log(isPalindrome('Anna')) // true
console.log(isPalindrome('Carlos')) // false
console.log(isPalindrome(101)) // TS Error
console.log(isPalindrome(true)) // TS Error
console.log(isPalindrome(false)) // TS Error
So, if you try to pass a number to the function, you will get the following error:
That's why TypeScript is very useful because it will force you to be more strict and explicit with your code.
Types
In the last example, we saw how to specify some primitive types for our function parameter and returned value, but you're probably wondering how you can describe an object or array with more details. Types can help us to describe our objects or arrays in a better way. For example, let's suppose you want to describe a User type to save the information into the database:
type User = {
username: string
email: string
name: string
age: number
website: string
active: boolean
}
const user: User = {
username: 'czantany',
email: 'carlos@milkzoft.com',
name: 'Carlos Santana',
age: 33,
website: 'http://www.js.education',
active: true
}
// Let's suppose you will insert this data using Sequelize...
models.User.create({ ...user }}
We get the following error if you forget to add one of the nodes or put an invalid value in one of them:
If you need optional nodes, you can always put a ? next to the name of the node, as shown in the following code block:
type User = {
username: string
email: string
name: string
age?: number
website: string
active: boolean
}
You can name type as you want, but a good practice to follow is to add a prefix of T, so, for example, the User type will become TUser. In this way, you can quickly recognize that it is type and you don't get confused thinking it is a class or a React component.
Interfaces
Interfaces are very similar to types and sometimes developers don't know the differences between them. Interfaces can be used to describe the shape of an object or function signature just like types, but the syntax is different:
interface User {
username: string
email: string
name: string
age?: number
website: string
active: boolean
}
You can name an interface as you want, but a good practice to follow is to add a prefix of I, so, for example, the User interface will become IUser. In this way, you can quickly recognize that it is an interface and you don't get confused thinking it is a class or a React component.
An interface can also be extended, implemented, and merged.
Extending
An interface or type can also be extended, but again the syntax will differ, as shown in the following code block:
// Extending an interface
interface IWork {
company: string
position: string
}
interface IPerson extends IWork {
name: string
age: number
}
// Extending a type
type TWork = {
company: string
position: string
}
type TPerson = TWork & {
name: string
age: number
}
// Extending an interface into a type
interface IWork {
company: string
position: string
}
type TPerson = IWork & {
name: string
age: number
}
As you can see, by using the & character, you can extend a type, while you extend an interface using the extends keyword.
Implementing
A class can implement an interface or type alias in the same exact way. But it cannot implement (or extend) a type alias that names a union type, for example:
// Implementing an interface
interface IWork {
company: string
position: string
}
class Person implements IWork {
name: 'Carlos'
age: 33
}
// Implementing a type
type TWork = {
company: string
position: string
}
class Person2 implements TWork {
name: 'Cristina'
age: 32
}
// You can't implement a union type
type TWork2 = { company: string; position: string } | { name: string; age: number }
class Person3 implements TWork2 {
company: 'Google'
position: 'Senior Software Engineer'
}
If you write that code, you will get the following error in your editor:
As you can see, you are not able to implement a union type.
Declaration merging
Unlike a type, an interface can be defined multiple times and will be treated as a single interface (all declarations will be merged), as shown in the following code block:
interface IUser {
username: string
email: string
name: string
age?: number
website: string
active: boolean
}
interface IUser {
country: string
}
const user: IUser = {
username: 'czantany',
email: 'carlos@milkzoft.com',
name: 'Carlos Santana',
country: 'Mexico',
age: 33,
website: 'http://www.js.education',
active: true
}
This is very useful when you need to extend your interfaces in different scenarios by just re-defining the same interface.