TS Cheatsheet
Types by Inference
const program = "Hello World"
// %inferred type: string
Type annotations
const numberToString = (value: number) : string => String(value)
console.log(numberToString(333))
/**
[LOG]: "333"
**/
Interfaces
Use interface if exporting so that consumers can extend.
interface Point {
x: number;
y: number;
}
const logPoint = (p: Point) => console.log(`${p.x}, ${p.y}`)
logPoint({ x: 333, y: 999})
/**
[LOG]: "333, 999"
**/
interface PersonInterface {
name: string
gender: string
}
interface EmployeeInterface extends PersonInterface {
code: number
}
const person: EmployeeInterface = {
name: "Josoroma",
gender: "M",
code: 1,
}
console.log(person)
/**
[LOG]: {
"name": "Josoroma",
"gender": "M",
"code": 1
}
**/
interface Component {
w: number
h: number
enableEvents(enable: boolean): void
}
interface Button extends Component {
label: string
}
class RadioButton implements Button {
h: number
w: number
label: string
private enable: boolean = false
constructor(h: number, w: number, label: string) {
this.h = h;
this.w = w;
this.label = label;
}
enableEvents(enable: boolean): void {
this.enable = enable;
}
}
const radioButton: Button = new RadioButton(100, 20, "test")
radioButton.enableEvents(true)
console.log(radioButton)
/**
[LOG]: RadioButton: {
"enable": true,
"h": 100,
"w": 20,
"label": "test"
}
**/
Intersect types
type FirstNameType = {
firstName: string
}
type LastNameType = {
lastName: string
}
type FullnameType = FirstNameType & LastNameType
const some: FullnameType = {
firstName: 'Jos',
lastName: 'oRoma',
}
console.log(some)
/**
[LOG]: {
"firstName": "Jos",
"lastName": "oRoma"
}
**/
Explicit object shape using types and interfaces
/** unions with exact string literal **/
type LanguageType = 'en' | 'fr' | 'es'
interface ComponentPropsInterface {
id: string
name: string
age: number
disabled: boolean
optional?: number[]
lang: LanguageType
children: ReactNode
onChange?: FormEventHandler<HTMLInputElement>
}
export Component(props: ComponentPropsInterface) {
const {id, name, age, disabled, lang} = props
return (
<>
{ children }
</>
);
}
As a function type
interface KeyInterface
{
(key: string, value: boolean): void
}
const addKeyValue = (key: string, value: boolean): void => {
console.log(`addKeyValue: key = ${key}, value = ${value}`)
}
const updateKeyValue = (key: string, value: boolean): void => {
console.log(`updateKeyValue: key = ${key}, value = ${value}`)
}
let attr: KeyInterface = addKeyValue
attr('disabled', true)
attr('enabled', false)
attr = updateKeyValue
attr('disabled', false)
/**
[LOG]: "addKeyValue: key = disabled, value = true"
[LOG]: "addKeyValue: key = enabled, value = false"
[LOG]: "updateKeyValue: key = disabled, value = false"
**/
Objects
interface ObjectProps {
/** array of strings */
names: string[]
/** any object (NOT COMMON but useful as placeholder) **/
obj: object
/** almost the same as `object`, exactly the same as `Object` **/
obj2: {}
/** an object with any number of properties (PREFERRED) **/
obj3: {
id: string
title: string
}
/** array of objects! (common) **/
objArr: {
id: string
title: string
}[]
}
Dictionaires
interface DictionaryProps {
/** a dict object with any number of properties of the same type **/
dict1: {
[key: string]: boolean
}
/** equivalent to dict1 **/
dict2: Record<string, boolean>
}
type Book = {
title: string
description: string
}
const bookDictionary: { [key: number]: Book } = {}
// Create book
bookDictionary[1] = {
title: 'The Law',
description: 'Originally published as a pamphlet, in 1850 by Frederic Bastiat.'
}
console.log(bookDictionary)
/**
[LOG]: {
"1": {
"title": "The Law",
"description": "Originally published as a pamphlet, in 1850 by Frederic Bastiat."
}
}
**/
Utility types
interface Book {
name: string
description?: string
year: number
}
Partial (Optional)
We can see:
- That two fields are required: name and year.
- The description field is optional.
When would you use a Partial?
Helpful when you are required to use only certain fields.
When we have a row of a table that represents in its columns the different fields of a record that can be updated, it can be any of the fields, so you don’t mind assuming that all fields may be ultimately optional seen as optional.
How partial works:
type Partial<T> = {
[P in keyof T]?: T[P]
};
It just defines a new object type which copies all properties of T as optional.
Required ()
interface Book {
id?: string
name: string
}
We can use this Book interface skipping the ID when creating the book. On the other hand, when we are required to update the book, we want to make sure the ID is set.
Using Map in TypeScript
type JobInfo = {
language: string
workExperience: number
}
type JobPosition = 'Frontend' | 'Backend'
const dictionaryJob = new Map<JobPosition, JobInfo>()
dictionaryJob.set('Frontend', {
language: 'JavaScript',
workExperience: 5
})
dictionaryJob.set('Backend', {
language: 'Python',
workExperience: 4
})
console.log(dictionaryJob)
/**
[LOG]: Map (2) {"Frontend" => {
"language": "JavaScript",
"workExperience": 5
}, "Backend" => {
"language": "Python",
"workExperience": 4
}}
**/
React Events
const handleOnChange = async (e: FormEvent<HTMLInputElement>) => {
if (!e.target.files) {
return;
}
// handle the input...
console.log(e.target.files);
}
return (
<>
<input type="text" value="my value" onChange={handleOnChange} />
</>
)
Typing default props
// using typeof as a shortcut; note that it hoists!
type GreetProps = { age: number } & typeof defaultProps
const defaultProps = {
age: 21,
}
const Greet = (props: GreetProps) => {
return props
}
Greet.defaultProps = defaultProps
console.log(Greet.defaultProps)
/**
[LOG]: {
"age": 21
}
**/
Function Components
interface AppProps {
message: string;
}
// declare a Function Component; return type is inferred.
const App = (props: AppProps) => <>{ props.message }</>
// inline the type declaration; eliminates naming the prop types, but looks repetitive.
const App = (props: { message: string }) => <>{ props.message }</>
// annotate the return type, so an error is raised if you accidentally return some other type.
const App = (props: AppProps): JSX.Element => <>{ props.message }</>
Hooks
// many hooks are initialized with null-ish default values.
const [user, setUser] = useState<UserInterface | null>(null)
// if a state is initialized soon after setup and always has a value after.
const [user, setUser] = React.useState<UserInterface>({} as UserInterface);
useEffect and useRef
const Foo = () => {
// - If possible, prefer as specific as possible. For example, HTMLDivElement
// is better than HTMLElement and way better than Element.
// - Technical-wise, this returns RefObject<HTMLDivElement>
const divRef = useRef<HTMLDivElement>(null)
useEffect(() => {
// Note that ref.current may be null. This is expected, because you may
// conditionally render the ref-ed element, or you may forgot to assign it
if (!divRef.current) throw Error("divRef is not assigned")
// Now divRef.current is sure to be HTMLDivElement
doSomethingWith(divRef.current)
}, []);
// Give the ref to an element so React can manage it for you.
return <div ref={divRef}>etc</div>
}
If you are sure that divRef.current will never be null, it is also possible to use the non-null assertion operator !
const divRef = useRef<HTMLDivElement>(null!)
// Later... No need to check if it is null.
doSomethingWith(divRef.current)