{"compilerOptions": {"jsx":"react",// transform TSX files into JSX"strict":true,// enable strict features"module":"commonjs","allowJs":true,// check and compile JS"target":"es2017",// target environment"outDir":"lib","declaration":true,// create *.d.ts files"sourceMap":true,// create source map for debug },"include": ["src"],// input files}
Basics
let x ="hello world"; // x is of type stringx ="hello mars"; // works OKx =42; // ERROR - number is not assignable to stringconsty="hello world"; // type is "hello world"
let z; // not initialized, type is any (top type)z =41; // allowed because number is subtype of anyz ="abc"; // same as abovelet zz:number; // explicit type annotationzz =41;zz ="abc"; // ERROR - string is not assignable to numeber
let aa:number[] = []; // aa is of type array of numberaa.push(33); // works OKaa.push("abc"); // ERROR - cannot add a string into a number array
let bb: [number,string,string,number] = [123,"Fake Street","Nowhere, USA",10110]; // bb is a tuple - array of fixed lengthbb = [1,2,3]; // ERROR - second and third arguments are string
let cc: { houseNumber:number; streetName:string };cc = { streetName:"Fake Street", houseNumber:123};cc = { houseNumber:33}; // ERROR - all members are required by defaultlet dd: { houseNumber:number; streetName?:string };dd = { houseNumber:33}; // OK - streetName? is now optionalinterfaceAddress { houseNumber:number; streetName?:string;}let ee:Address= { houseNumber:33 };
exportinterfaceHasPhoneNumber { name:string; phone:number;}exportinterfaceHasEmail { name:string; email:string;}let contactInfo:HasEmail|HasPhoneNumner; // intersection typecontactInfo.name ="Mike"; // we can access only name, because it's in common between the interfaceslet otherContactInfo:HasEmail&HasPhoneNumber= { name:"Mike", email:"mike@example.com", phone:3215551212}; // union type - must be initialized with all fieldsotherContactInfo.name;otherContactInfo.email;otherContactInfo.phone; // we can access all
Functions
// Classical functionsfunctionsendEmail(to:HasEmail): { recipient:string; body:string } {return { recipient:`${to.name} <${to.email}>` body: "You're pre-qualified for a loan!" };}// Arrow-head notationconstsendTextMessage= ( to:HasPhoneNumber): { recipient:string; body:string } => {return { recipient:`${to.name} <${to.phone}>` body: "You're pre-qualified for a loan!" };};
// overload signaturesfunctioncontactPeople(method:"email",...people:HasEmail[]):void;functioncontactPeople(method:"phone",...people:HasPhoneNumber[]):void;// function implementationfunctioncontactPeople( method:"email"|"phone",...people: (HasEmail|HasPhoneNumber)[]):void {if (method ==="email") { (people asHasEmail[]).forEarch(sendEmail); } else { (people asHasPhoneNumber[]).forEach(sendTextMessage); }} // overload signatures prevent cases of method "email" but people of type HasPhoneNumber
Where possible, provide a specific and appropriate type (explicit any otherwise)
get tests passing again
Squash explicit anys, enable strict mode
Enable strict mode in tsconfig.json
Replace explicit anys with more appropriate types (incrementally, in small chunks)
Generics
interfaceWrappedValue<X> { value:X;}let val:WrappedValue<string> = { value:'' };val.value; // value is of type string
interfaceFilterFunction<T=any> { (val:T):boolean}conststringFilter:FilterFunction<string> = val =>typeof val ==="string";stringFilter(0); // ERROR - 0 is not a stringstringFilter("abc"); // OKconsttruthyFilter:FilterFunction= val => valtruthyFilter(0); // false - valid because T is any because there was no typetruthyFilter(1); // true - valid because T is any because there was no type
functionarrayToDict<Textends { id:string }>(array:T[]): { [k:string]:T} {constout: { [k:string]:T } = {};arrayForEach(val => { out[val.id] = val; // without extends {id: string} this line will not work });return out;}
Top and Bottom types
let myAny:any=32; // all values can be assigned to anylet myUnknown:unknown="hello, unknown"; // all value can be assigned to unknownmyAny.foo.bar.baz; // validmyUnknown.foo; // ERROR!if (typeof myUnknown ==="string") {myUnknown.split(", "); // OK}// user-defined type guardsfunctionisHasEmail(x:any): x isHasEmail {returntypeofx.name ==="string"&&typeofx.email ==="string";}if (isHasEmail(myUnknown)) {// in here, myUnknown is of type HasEmailconsole.log(myUnknown.name,myUnknown.email);}
let n:never=4; // all values can be assigned to neverlet x ="abc"asstring|number;if (typeof x ==="string") {// x is a string herex.split(", ");} elseif (typeof x ==="number") {// x is a number herex.toFixed(2);} else {// x is a never here}