You have a basic knowledge of ES6 and want to learn you a TypeScript for great good! Well, you've come to the right place!
This workshop is meant to help you, a seasoned JS-developer, to get up to speed with TypeScript in no time!
This is an early version and there is quite some potential to improve the coding-parts in particular.
yarn yarn start
You also might want to install a TypeScript-plugin for your editor before starting!
To following Sections are supposed to be read/worked in order.
An example of expressing that color should always be a string:
let color: string = "blue";
All the types:
-
boolean
-
string
-
number
- keep in mind that everything in JavaScript is a float. -
Arrays
number[]
Array<number>
-
Tuples Basically fixed size arrays.
[string, number | boolean]
-
Enum Maps readable identifiers to numbers. Starts to count at 0.
enum Color {Red, Green, Blue}
enum Color {Red = 1, Green, Blue}
- starts the enumeration at 1enum Color {Red = 1, Green = 2, Blue = 4}
- you can set all the values by hand
-
any
- thing can be of any kind. this is equivalent to completely skipping type-checks. avoid this! -
void
-void
is a little like the opposite ofany
: the absence of having a type. you may commonly see this as the return type of functions that do not return a value. -
null
,undefined
- By defaultnull
andundefined
are subtypes of all other types. That means you can assignnull
andundefined
to something likenumber
. However, when using the--strictNullChecks
flag,null
andundefined
are only assignable to void and their respective types. This helps avoid many common errors. In cases where you want to pass in either astring
ornull
orundefined
, you can use a union type (you'll read about them later):string | null | undefined
. -
never
- represents the type of values that never occur. For instance, never is the return type for a function expression or an arrow function expression that always throws an exception or one that never returns:function error(message: string): never { throw new Error(message); // We `never` get here :) } // Inferred return type is never function fail() { return error("Something failed"); } function infiniteLoop(): never { while (true) {} // We `never` get here. And yes, the compiler is clever enough to know! }
If you should ever need more info, click here.
Our customer just reported a critical issue!
The App at http://localhost:3333
falsely greets with Hello, [object Object]
! Fortunately Alice recently introduced TypeScript to the project. Unfortunately she was in a hurry and used any
-annotations everywhere to make the compiler happy!
Narrow down the types in app/app.ts
and afterwards let the compiler guide you to finding the bug! This has to happen ASAP (as always)!
Type assertions are a way to tell the compiler "trust me, I know what I’m doing". There are 2 ways to do this:
let strLength: number = (<string>someValue).length; let strLength: number = (someValue as string).length;
- For Objects
interface Point { readonly x: number; readonly y: number; description: string; }
- For Functions
interface SearchFunc { (source: string, subString: string): boolean; } let mySearch: SearchFunc; mySearch = function(source: string, subString: string) { let result = source.search(subString); return result > -1; }
- Indexable Types
interface StringArray { [index: number]: string; } let myArray: StringArray = ["Bob", "Fred"]; let myStr: string = myArray[0];
This example is not complete and you don't need to know this by heart just yet. If you remember that there's some magic needed to make myVar[0]
work, you're good. In case you remembered and came to look for more information, please go somewhere else!.
- Class Interfaces
interface ClockInterface { private currentTime: Date; setTime(d: Date); new (hour: number, minute: number); } class Clock implements ClockInterface { private currentTime; setTime(d) { this.currentTime = d; } constructor(h, m) { } // note how the params don't need to match the names from the interface }
Add a JJ
. A JJ
is functionally equivalent to a Greeter
except it always greets with Yo, digga!
. Let your JJ
inherit from Greeter
!
Remember! Work those bullets one after another!
- If your
JJ
now needs a string for instantiation, teach it to work without that! - If your
JJ
now contains a constructor, please remove it. You might need to tell the Greeter to just greetyou
by default. - If somebody could obtain your default value using something like
(new Greeter())['greeting']
make sure to protect your private parts! - Note that
constructor(private greeting : string) {
will automatically create a private instance-variable calledgreeting
. Can you golf your code with this? - The customer decided that the greeting-prefix ("Hallo,") should be customizable! Extract an
Greeting
-Interface and adapt you code.
class Shape { private position: any; } interface DrawableShape extends Shape { draw(): void; } class DrawableCircle implements DrawableShape { radius: number; }
Yip, that's right! Classes can also extend interfaces and the other way around.
When an interface type extends a class type it inherits the members of the class but not their implementations. Interfaces inherit even the private and protected members of a base class.
- accessors
let passcode = "secret passcode"; class Employee { private _fullName: string; get fullName(): string { return this._fullName; } set fullName(newName: string) { if (passcode && passcode == "secret passcode") { this._fullName = newName; } else { console.log("Error: Unauthorized update of employee!"); } } } let employee = new Employee(); employee.fullName = "Bob Smith"; if (employee.fullName) { console.log(employee.fullName); }
- Static Properties
class JJ { static greeting: string; }
- Abstract Classes
They may not be instantiated directly, but can contain implementation (which interfaces can not).
abstract class Animal { abstract makeSound(): void; move(): void { console.log("roaming the earth..."); } }
- inline-types have other syntax than their interface-pendant
let myAdd: (baseValue:number, increment:number) => number = (x, y) => x + y;;
- Rest Params
function buildName(firstName: string, ...restOfName: string[]) {
class GenericNumber<T> { zeroValue: T; add: (x: T, y: T) => T; } let myGenericNumber = new GenericNumber<number>(); myGenericNumber.zeroValue = 0; myGenericNumber.add = (x, y) => x + y;
- Intersection Types
Person & Serializable & Loggable
is a Person
and Serializable
and Loggable
.
- Union Types
Person | JJ
is either a Person
or a JJ
.
interface Bird { fly(); layEggs(); } interface Fish { swim(); layEggs(); } function getSmallPet(): Fish | Bird {}
You can safely assume that Bob is working for Customer.
Implement:
Eve
caneatCookie
,callCustomer
,rantAboutBob
,manageDev
Alice
canreviewCode
,callCustomer
,rantAboutBob
,code
Bob
canreviewCode
,eatCookie
,callCustomer
,rantAboutBob
,code
Carol
canreviewCode
,callCustomer
,rantAboutBob
,code
- A function that lets someone review some code and then code (to fix that idiot's stuff of course!).
- A function that lets someone eat a cookie before calling the customer and rant about Bob.
- A function that lets someone eat a cookie.
Experiment:
- Feed different people to the functions look at the compiler messages.
- Have at least one intersection type
- Have at least one union type
Of course you followed along really vigilant and rightfully ask: "what if i destructure something and alias it so something named like a type?". I knew you were the type of person that wants to make compilers mad... So let's see:
let o = {a: "someString", b: "someNumber"} // v1 (bad) let { a: string, b: number } = o; console.log(a) // => compiler error: "Cannot find name 'a'" console.log(string) // => "someString". WAIT? WHAT? // v2 (good) let { a: string, b: number }: { a: string, b: number } = o; console.log(a) // => "someString". YAY! console.log(string) // => compiler error: "Cannot find name 'string"
As all valid Javascript is supposed to be valid TypeScript we CAN name variables string
or number
. While thinking through the example above you hopefully saw that this is a rather bad idea when using TypeScript.
Here's an equivalent for arrays:
function f([first, second]: [number, number]) {}
- Compile a
.ts
-file by hand. Hint: you'll need a TypeScript-project - which means atsconfig.json
. - Learn about the configuration options in
tsconfig.json
. - Learn about the Compiler Options.
- Learn about "Declaration Files".
- Search the interwebs for
tslint
for even more order.
- Rank the workshop from 1 (really bad) to 10 (really good).
- Think of at least 3 predicates that came on your mind while thinking of a rating.
- Write down each of the predicates and rank them separately. This might be something like:
- The workshop started on time - 2
- I don't like those powerpoint-slides - 0
- The cat picture made my day - 10
- I learned a little - 6
- Submit this along with any additional Feedback you might have.