TypeScript in my opinion will always remain a superhero large scale application development tool, TypeScript comes loaded with a great type system and in this article i am going to write about arguably one of the biggest features of TypeScript which is the Type System.
But why Add Types?
This is one of the first questions i asked myself when i stumbled accross TypeScript, if valid JavaScript is TypeScript why bother adding extra codes to my already existing code base, now i see the importance of using TypeScript and i don't see myself working on a large scale application and using JavaScript, that's not just possible.
A Type system is there because it gives meaning to our code. A Type System coerces some form of sense and orderliness to our code. In JavaScript we can pass invalid types as arguments to a function. We can pass less number of arguments to the function or even more arguments than is required and all that will be fine until runtime. But you are working for an institution that is paying you hard earned dollars and time is money. How bout we catch this types of bugs before runtime? Enter a Type system.
To read more articles like this please visit Netcreed
A Type system goal is to provide a type definition for each variable, function, class, object. The types defined for each is used at compile time to perform some checks to ensure that each value assigned to a variable is of the type annotated with variable it is assigned to and if not, to expose the errors relating to wrong type of value that is passed to a variable.
TypeScript is statically typed because unlike JavaScript that performs this checks during runtime, the TypeScript compiler will actually perform this check when we try to compile our TypeScript code to JavaScript even as we are writing our code the TypeScript compiler is actually doing it's work and will notify us when we try to do something that's not valid.
Primitive Types And Functions
TypeScript provides types for all primitive values in JavaScript as we have seen from earlier articles. Functions can also be typed but instead they are called signatures.
A function signature specifies the number and type of arguments the function can accept. It also specifies the return type of the function.
// STRING
let name: string,
name='sam' // OR name = "sam"
// NUMBER
let age: number
age = 201 //
// BOOLEAN
let isOnline: boolean
// function signature
let sayHello: (person: string) => string
sayHello can only accept a string and must return a string otherwise there will be a compile error.
Arrays are central to working with JavaScript and thus TypeScript also allows for type annotations with arrays.
// STRING ARRAY
let names: string[] = ['becker', 'ahmed', 'james']
names = [1, false, 17] // Not Okay
// NUMBER ARRAY
let prices: number[] = [1, 11, 7]
prices = ['shoes'] // Not Okay
To gain more control over what element occupies a particular index in an array we TypeScript provides tuples. A tuple is a kind of array where each index of the array can only store a particular type of value.
// TUPLES
let arr :[number, string, boolean]
arr = [1, 'becker', true] // Okay
arr = [false, 'becker', 1] // Not Okay
The array above can only store a number in it's first index, a string in it's second index and a boolean in the third index. Tuples are quite good when using the rest operator.
We can use interfaces to define the structure of an object or the shape of a class, or to combine multiple type definitions into a single type, an example of an interface is presented below;
interface Car {
wheels: number,
color: string,
plateNumber: string,
manufacturer: string,
model: string
}
// Okay satisfies the contract
let lambo: Car = {
wheels: 4,
color: 'red',
plateNumber: '234RE2',
manufacturer: 'Lamborghini',
model: 'sesto elemento'
}
// Not okay must satisfy the contract
let randCar : Car = {
wheels: '2',
plateNo: 23424,
}
Union | Custom Types
Typescript also provide type alias for creating custom types and union types. Union types are for annotating variables that can store more than one type of value. While custom types allow us to create our own types from a primitive type or another type we created. We can also use literal values for type definition. When we do that any variable whose type or function whose signature accepts or returns that type will all deal with the literal value.
// TYPE ALIAS
type color: = 'red'
// COMBINING WITH UNION TYPES
type carColor = 'red' | 'green' | 'blue' | 'yellow'
// UNION TYPES
let plateNumber: string | number
let lamboColor:carColor = 'red' // Okay
lamboColor = 'purple' // Not Okay
TypeScript's Type System
TypeScript type system originated from the type theory developed by Bertrand Russell who developed the theory in the early 20th century. The type theory is a system where each term is given a type and operations are restricted based on the types, if we pull up a comparison between TypeScript's type annotation and the type theory we will find a great detail of striking similarity.
// TYPE THEORY
z: nat
clickZ: nat -> nat
This is a basic example of type theory building blocks, let's take a look at type annotations in TypeScript.
//TYPESCRIPT'S TYPE ANNOTATION
let num: number
let logNum: (num: number) => number;
You see the similarity i spoke about earlier? Let's go on to discuss some attributes of TypeScripts type system.
Optional Static Typing
TypeScript is the product of the lessons learned from working with strongly typed languages like java and C#. So TypeScript comes with the benefit of optional typing. Plus TypeScript is a superset of JavaScript, we all know that JavaScript is dynamically types. Although this is not too good, but it comes with some benefits. Rather than being in a spaghetti like situation where you feel like you are typing your self to death. You can tell the TypeScript compiler to come of easy with the types because you don't know the actual type the variable will hold untill you assign a value to it. This can be a huge breather and gives you a sense of freedom and being in control.
// When we know the type of a value
let name: string = 'supes'
// When we don't know the type of value a hero will hold
let hero: any
hero = 'superman'
// OR
hero = {name}
// OR
hero = true
// OR
hero = 3
Incase you feel confused about the shape of your object or the type of value it should store, just annotate it with any
and you can work much like you do in JavaScript.
Type Inference
Another cool feature of the Type system employed by TypeScript is that if you don't specify the type for a variable, TypeScript will automatically infer the type of the value you pass to variable to it. And it leans towards making our code short and more clean especially if you assigning a value to a variable immediately after it is created. You don't actually need to annotate the variable with the type because that is really redundant.
//INSTEAD OF
let name: string = 'supes'
//RATHER USE
let job = 'coding'
let age = 20
// TypeScript will auto infer the string type to job
// and number to age
job = 600 // Not okay
age = false // Not okay
If you plan to write code that is like above where you do things the JavaScript way, remember to annotate the variable with the any
type.
Structural Typing
Unlike the early strongly typed language that uses a nominal typing system, TypeScript uses a structural typing system. But wait what is a structural typing system and what is a nominal typing system? In nominal typing system a variable is only of a valid type when we explicitly decorate the variable definition with that type.
Let's take a use case, we know that admin on a platform must be a user. In a nominal typing system an admin is not a user and only an admin. We have to explicitly decorate it with the interface for an admin for it to be valid. This kind of system prevents situation where an object with similar properties of an admin can be valid just because it looks like it. This is cool but i don't like this approach personally. And that's where structural Typing comes in to play.
Structural Typing system is actually concerned with the internal structure of an object, that is to say as far as an admin and a user has the same structure, a user is as valid as an admin. This kind of effect with structural typing is actually desired in TypeScript. We can also achieve the same result that a nominal typing system gives us with TypeScript. Let see TypeScript's structural typing system in play
type user = {
name: string,
id: string
}
let sayHello : (obj: user) => string
let sam: user = {
name: 'sam',
id: '1'
}
let superAdmin = {
name: 'super',
id: '11'
}
sayHello = obj:user => return `${obj.name} says hello`;
// VALID
console.log(sayHello(sam)) // sam says hello
// VALID
console.log(sayHello(superAdmin)) // super says hello
If we wanted to achieve the nominal typing effect we can make use of generics, let's see a typical implementation
type userId = 'user'
type adminId = 'admin'
type user<uid extends string> = {
name: string,
id: uid
}
let sayHello: (obj: user<userId>) => string
let sam:user<userId> = {
name: 'sam',
id: 'user'
}
let superAdmin = {
name: 'super',
id: 'admin'
}
// POSSIBLE
console.log(sayHello(sam)) // sam
// NOT POSSIBLE
conosle.log(sayHello(superAdmin))
// Will show error in IDE
Type Checking
One thing TypeScript does that makes our work much easier is type checking. Once we have defined the types for our variables TypeScript automatically go through each assignment in our code to ensure that for each variable defined the right type of value is assigned to it. For each function the right type of arguments are called with the function. It will also ensure that the function receives the right number of arguments.
let callPerson: (phoneNo: number) => string
callPerson = (phoneNo) => `calling ${phoneNo}...`
let callKala = callPerson(234804568890); // Okay
let callFrank = callPerson('234804568890') // Not Okay
callKala = 23 // Not Okay coz callKala is a string, type inference
As we work with more complex objects and type definitions, TypeScript will test each property on each object. It will even check that each class has the right type of access modifiers for properties and in turn that they are expecting the same type and that they actually receive the right type of value. If the Object contains nested within it another object the same level of type checking will be performed on the object.
Widened Types
A widened type is a typical situation of a function call that returns null
or undefined
. An expression that returns either of the two also fits into
this category. And an assignment whose type is null.
let log = () => null
let widened = log()
Type Erasure
When we compile our TypeScript code to JavaScript, the compiler will erase all type definitions, function signatures and interfaces from the compiled JavaScript code. This is because JavaScript as we know it does not support types.
That's it, hope you enjoyed it and found it useful. Personally my experience working with typescript has been superb, stay tuned for articles on TypeScript.
To read more articles like this please visit Netcreed
Top comments (11)
Just know that TypeScript doesn't have your back as much as other options:
A Note on Soundness
Compare with Rescript:
Also check out Hegel - a static type-checker for JS that attempts to achieve soundness, which also enables far more powerful inference.
Obviously, this project is new, and has to basically "start over" on many of the things that TS added over the years. To avoid that, they do try to leverage existing
.d.ts
files, but this only seems to work for the most basic type-definitions right now, and man do they have their work cut out for them.Still, this is definitely worth a look - I would strongly encourage anyone to at least spend 20 minutes poking around in the online playground to see what this is about. If you don't already know something like Elm or ReScript, you will be surprised by the safety you get from sound type-checking, and with a much smaller amount of manually type-hinting compared with TS.
This project is so overlooked, and really deserves a lot more attention than it gets. I think maybe some people are already fatigued by the constant growing complexity of TS, and my own early reaction was something like "ugh, another type-checker?" - but this really is a whole different ball game. Don't skip this one! 🙂
Given
and
(i.e. Hegel is a tool rather than a language) it seems that comment syntax should be a "rather sooner than later" feature
Rescript has a lot of other syntax changes that go along with it, many of them opinionated at that. There's enough differences beyond just Typing that prevent me from ever wanting to use it.
Hmmm... Thanks for the heads up.. I think i need to get familiar with Rescript and check more about "soundness"
Look, given how the JavaScript ecosystem works (i.e. tendency to be heavy on external dependencies) it's not all sunshine and roses in the ReScript world because JavaScript Interop is a topic in and onto itself.
I still believe that TypeScript adoption has less to do with the sincere desire (or need) to adopt static typing but more with VS Code's TypeScript support and the associated developer experience.
TypeScript knowingly limited the "type safety" potential by basing its syntax on JavaScript (prioritizing JavaScript interoperability) but then promptly introduced its own non-compatible syntax requiring transpilation.
At this point one cannot get around TypeScript. However I think it's a good idea to get familiar with JSDoc TypeScript. That way one can choose to author in JavaScript, not having to transpile all the time but simply run TypeScript occasionally as a super-linter.
Personally I still view TypeScript as a missed opportunity. Given the cost of compilation (and having to learn additional syntax) there was a real opportunity to use modern static typing (e.g. Rust vs C/C++).
Quote
Microsoft introduced F# in 2005 and TypeScript didn't appear until 7 years later in 2012 - but ultimately the C# way of working and thinking prevaled (given that JavaScript dates back to 1995 I don't see 17 years worth of improvement in TypeScript).
See also Here is why you might NOT want to use TypeScript, The Trouble with TypeScript.
Just TS took enough time, but solve only simplest errors, those can be easily be caught by tests.
Most complex and time consuming errors come from misunderstanding of requirements, trying to use libraries wrong way, just bad code and no refactoring.
Mostly TS helps with autocomplete, very little benefits in long run.
researchgate.net/publication/25963...
medium.com/javascript-scene/the-sh...
In my opinion TypeScript provides much more than auto-complete. TypeScript is a large scale application tool that is employed by various software engineers and companies to build large scale web apps. TypeScript also aids a lot with documentation, throw in jsdoc to the mix and you have a great developer experience. Outside this TypeScript allows you to use out of the box features that are not yet supported in the browser... All the reasons for errors you gave above are actually true, if you have consumed any popular library you work with on JavaScript with TypeScript, you will understand the role TypeScript plays. The numbers speaks for themselves. Just check up how many people is using TypeScript.
How many people use something doesn't mean it's automatically best option.
I remember GraphQL client for react, it's written on TS, doesn't change that is buggy as hell.
Another great article
medium.com/javascript-scene/the-ty...
Still need to learn pure JS, even weird parts. Still need to test code, testing in my experience makes huge impact, code review also.
Time and resources limited, time spent on fighting TS is time not spent on tests and refactoring, i see it a few times, didn't see other way around yet.
I've been considering learning TypeScript, and thankfully I found this article. Definitely will use this as a guide, and bookmarked it too! Thanks for the info!
i am glad that you found it useful bro