-
Notifications
You must be signed in to change notification settings - Fork 41
Typescript: (2) Creating Basic Types
const numbers: Array<number> = []
const item: string[] = []
function logScores(firstName: string, ...scores: number[]){
console.log(firstName, scores)
}
logScores("Mike", 90, 60, 65)
// logScores("Mike", 90, 60, "65")
// Argument of type 'string' is not assignable to parameter of type 'number'.// you have to annotate this
const tomScore: [string, number] = ["tom", 70]
// because if you don't, it will infer it to be
// of type (string | number)[]Open ended tuple - items have some structure but the number of items isn't fixed
const benScores: [string, ...number[]] = ["ben", 50, 75, 85]const tomScore = { name: "tom", score: 70 }
// type will be infered as {name: string; score: number}
tomScore.score = 75 // this will not trigger an error
tomScore.passed = true // will trigger an error
// because passed doesnt exist on the inferred typeExplicit object type annotations
const tomScore: {name: string; score: number; } = {
name: "Tom",
score: 70
}type Score = {name: string; score: number;}
const tomScore: Score = { name: "Tom", score: 70 }
const bobScore: Score = { name: "Bob", score: 80 }// onClick is optional
interface ButtonProps {
text: string;
onClick?: () => void;
}
const BuyButton: ButtonProps = {
text: "Buy",
onClick: () => console.log("Buy")
}Make things read-only
interface ButtonProps {
readonly text: string;
}
const buyButton: ButtonProps = {
text: "Buy"
}
BuyButton.text = "Sell" // ERROR
interface Result {
readonly name: string;
readonly score: number[];
}
let billScores: Result = {
name: "Bill",
scores: [90, 65, 80
}
// Important! This will NOT trigger an ERROR
billScores.scores.push(70)
interface ImmutableResult {
readonly name: string;
readonly scores: readonly number[];
}
let tomScores: ImmutableResult = {
name: "Tom",
scores: [50, 95, 80]
}
tomScores.scores.push(70) // Will trigger an ERROR!Extending interfaces
interface ColoredButtonProps extends ButtonProps {
color: string;
}Interfaces vs type aliases types and interfaces are similar and it is generally personal preferences as to which approach to use when creating types. before there where more differences, but now they're very similar.
let age: number | null |undefined
let possibleFruits: "banana" | "apple" | "pear"
// might be useful for react reducers
type Actions = { type: "loading" } | { type: "loaded", data: { name: string } }type Name = {
firstName: string;
lastName: string;
}
type PhoneNumber = {
landline: string;
mobile: string;
}
type Contact = Name & PhoneNumber
const fred: Contact {
firstName: "Fred",
lastName: "Smith",
landline: "9425556"
mobile: "091780513337"
}
type Email = { emailAddress: string }
type ExtendedContact = Name & PhoneNumber & EmailThe code below will error
// IMPORTANT: This will create an error!
type BaseElement = {
name: string
kind: "text" | "number" | "email"
}
type TextInput = { kind: "text" }
type Field = BaseElement & TextInput
const age: Field = {
name: "Age",
kind: "number"
}
// Type '"number"' is not assignable to type '"text"'.
// when you intersect BaseElement & TextInput
// BaseElement has kind: "text" | "number" | "email"
// TextInput has kind: "text"
// THE COMMON MEMBER OF AN INTERSECTION TYPE
// IS MATHEMATICALLY INTERSECTEDAnother example
type A = {
doIt: (a: string) => void
}
type B = {
doIt: (a: string, b: string) => void
}
type A_and_B = A & B
// can also be `B & A` it doesn't matter
// this will NOT error
const ab1: A_and_B = {
doIt: (a: string ) => {}
}
// this will error
const ab2: A_and_B = {
doIt: (a:string, b: string) => {}
}interfaces can be extended, but type aliases can also extend existing types using the intersection operator
let firstName: string = "fred"
let age = 30
firstName = age
// ERROR: Type 'number' is not assignable to type 'string'.
const jones: "Tom" | "Bob" = "Tom"
let jane: string = "jane"
jane = jones // no errorType names are not important It's the structure that is important
type Person = { name: string }
interface IPerson { name: string }
const bob: Person = { name: "bob" }
const fred: IPerson = { name: "fred" }
let person = bob;
person = fred // Okaylet bob = { name: "bob" }
let oldFred = { name: "fred", age: 50 }
let person = bob
person = oldFred
// no error, even if oldFred has age, and person does not have that propertyBut this will error
let bob = { name: "bob" }
let oldFred = { name: "fred", age: 50 }
let person = bob
person = oldFred
oldFred = bob // ERROR
// Property 'age' is missing in type '{ name: string; }' but required in type '{ name: string; age: number; }'.Typescript uses structural typing which means variables with different types can be assigned to one another if the types are compatible
- var
acan be assigned to varbifbtype is wider thanatype - object
acan be assigned to objectbofahas at least the same members asb, (acan have properties thanbdon't haveacan still be assigned tob) - function
acan be assigned tobif each parameter inahas a corresponding parameter inbwith a compatible type
let multiply = (a: number, b: number, c: number): number => a * b * c
let subtract = (x: number, y: number ) => x - y
// subtract = multiply
// Type '(a: number, b: number, c: number) => number'
// is not assignable to type '(x: number, y: number) => number'.
multiply = subtract // no error (`subtract` has less parameters than `multiply`)IMPORTANT! Because a function with two parameters fits into a function with three parameters. If function parameters are a subset of the parameters of another function, it can be assigned to it WHY??