-
Notifications
You must be signed in to change notification settings - Fork 41
Typescript: (1) Basic Types
Mithi Sevilla edited this page Apr 9, 2021
·
8 revisions
- Typescript Exercises
- Advanced TypeScript Types Cheat Sheet (with Examples)
- Fettblog: Typescript Guide
- primitive types (
string,number,boolean,bigint,null,undefined,symbol) +object - loosely typed
- Typescript doesn't exist at runtime so it won't do time checking at runtime
- annotations are removed during the transpilation process
let score: number
console.log(score)
// Variable 'score' is used before being assigned.let score: number
score = 10
console.log(score) // no errorfunction add(a: number, b: number): number {
return a + b
}
const multiply = (a: number, b: number): number => a * b
function subtract(a: number, b?: number): number {
return a - (b || 0)
}- can infer types most of the time
- can infer
DateConstructortype
let dateOfBirth = new Date(1990, 4, 7)
// type of firstName is "bob"
const firstName = "bob"
// type of full name is string
let fullName = "bob"
// type of counter is any
let counter
counter = 10
// can infer that return type of add is a number
// no need to annotate
const add = (a: number, b: number) => a + b
// type of 10 is number
const ten = add(5, 5)
// return type is any
const addTen(a) => a + 10
// fourteen is of type any
const fourteen = addTen(4)- if variable is of type
any, typescript doesn't do type checking on it - we rarely need to use it
- when function doesn't return anything, it's return value is of type
void - a variable marked as
voidcan only holdundefinedornull -
voidis only really useful for function return types
- used to represent a type of value that will never occur
- we can use it to flag areas of code that shouldn't be reached explicitly
function outputMessage(message: string) {
if(typeof message === "string") {
console.log(message)
} else {
let invalid = message // invalid is of type message
console.error(invalid)
}
}- Similar to
anybut this one is typesafe
function add(a: unknown, b: unknown) {
return a + b
}
// error: Object is of type "unknown"
// errors are raised because variables of type unknown
// can't be added together// this works
function add(a: unknown, b: unknown) {
if(typeof a === "number" && typeof b === "number") {
return a + b
}
return 0
}async function getData(path: string): Promise<unknown> {
const response = await fetch(path)
return await response.json()
}
type Person = { id: string; name: string }
// the return type: person is Person
// is a type predicate
// a special return type that typescript compiler
// uses to know what type a particular value is
function isPerson(person: any): person is Person {
return "id" in person && "name" in person
}
async function getPerson(): Promise<Person | null> {
const person = await getData("/person/1")
if(person && isPerson(person)) {
return person
}
return null
}getAge() as number and <number>getAge() are the same
// not working as you might have expected
// but this is how typescript works as of writing
const getAge = () : any => { return "6" }
const age: number = getAge() as number + 2
console.log(age) // returns "62"Filter by either name, age, or occupation but NOT by type
interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}
export function filterUsers(persons: Person[], criteria: Partial<Omit<User, 'type'>>): User[] {
return persons.filter(isUser).filter((user) => {
const criteriaKeys = Object.keys(criteria) as (keyof Omit<User, 'type'>)[];
return criteriaKeys.every((fieldName) => {
return user[fieldName] === criteria[fieldName];
});
});
}Exercise:
Fix typing for the filterPersons so that it can filter users
and return User[] when personType='user' and return Admin[]
when personType='admin'. Also filterPersons should accept
partial User/Admin type according to the personType.
`criteria` argument should behave according to the
`personType` argument value. `type` field is not allowed in
the `criteria` field.
Higher difficulty bonus exercise:
Implement a function `getObjectKeys()` which returns more
convenient result for any argument given, so that you don't
need to cast it.
let criteriaKeys = Object.keys(criteria) as (keyof User)[];
-->
let criteriaKeys = getObjectKeys(criteria);
https://www.typescriptlang.org/docs/handbook/functions.html#overloads
interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}
interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}
export type Person = User | Admin;
export const persons: Person[] = [
{ type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
{ type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
{ type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' },
{ type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' },
{ type: 'user', name: 'Wilson', age: 23, occupation: 'Ball' },
{ type: 'admin', name: 'Agent Smith', age: 23, role: 'Anti-virus engineer' }
]
const getObjectKeys = <T>(obj: T) => Object.keys(obj) as (keyof T)[];
export function filterPersons(persons: Person[], personType: 'user', criteria: Partial<Omit<User, 'type'>>): User[]
export function filterPersons(persons: Person[], personType: 'admin', criteria: Partial<Omit<Admin, 'type'>>): Admin[]
export function filterPersons(persons: Person[], personType: string, criteria: Partial<Person>): Person[] {
return persons
.filter((person) => person.type === personType)
.filter((person) => {
let criteriaKeys = getObjectKeys(criteria)
return criteriaKeys.every((fieldName) => {
return person[fieldName] === criteria[fieldName];
});
});
}
export const usersOfAge23 = filterPersons(persons, 'user', { age: 23 });
export const adminsOfAge23 = filterPersons(persons, 'admin', { age: 23 });Below is typescript's official example but to be honest I won't implement this functionality this way
let suits = ["hearts", "spades", "clubs", "diamonds"];
function pickCard(x: { suit: string; card: number }[]): number;
function pickCard(x: number): { suit: string; card: number };
function pickCard(x: any): any {
// Check to see if we're working with an object/array
// if so, they gave us the deck and we'll pick the card
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// Otherwise just let them pick the card
else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
let myDeck = [
{ suit: "diamonds", card: 2 },
{ suit: "spades", card: 10 },
{ suit: "hearts", card: 4 },
];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);
let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);Implement swap which receives 2 persons and returns them in the reverse order
export type Person = User | Admin
export function swap<T1, T2>(v1: T1, v2: T2): [T2, T1] {
return [v2, v1];
}
function test1() {
console.log('test1:');
const [secondUser, firstAdmin] = swap(admins[0], users[1]);
logUser(secondUser);
logAdmin(firstAdmin);
}
function test2() {
console.log('test2:');
const [secondAdmin, firstUser] = swap(users[0], admins[1]);
logAdmin(secondAdmin);
logUser(firstUser);
}
function test3() {
console.log('test3:');
const [secondUser, firstUser] = swap(users[0], users[1]);
logUser(secondUser);
logUser(firstUser);
}Define type PowerUser which should have all fields
from both User and Admin (except for type),
and also have type 'powerUser' without duplicating
all the fields in the code.
type PowerUser = Omit<User, 'type'> & Omit<Admin, 'type'> & { type: 'powerUser' }The server API format was decided to be the following:
In case of success: { status: 'success', data: RESPONSE_DATA }
In case of error: { status: 'error', error: ERROR_MESSAGE }
The API engineer started creating types for this API and
quickly figured out that the amount of types needed to be
created is too big.
export type ApiResponse<T> = { status: 'success', data: T } | { status: 'error', error: string }
export function requestAdmins(callback: (response: ApiResponse<Admin[]>) => void) {
callback({
status: 'success',
data: admins
});
}
export function requestUsers(callback: (response: ApiResponse<User[]>) => void) {
callback({
status: 'success',
data: users
});
}
export function requestCurrentServerTime(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'success',
data: Date.now()
});
}
export function requestCoffeeMachineQueueLength(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'error',
error: 'Numeric value has exceeded Number.MAX_SAFE_INTEGER.'
});
}