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.