TypeScript Cheat Sheet
TypeScript cheat sheet, for quick reference only.
Quick Reference
Type
Interface
Control Flow Analysis
Class
Basics
/**
* Primitive types.
*/
const name: string = 'Jason Yu'
const age: number = 30
const hasMarried: boolean = true
const colors : string[] = ['red', 'blue']
/**
* Using `any` disables all further type checking, and it is assumed you know the environmen better than TypeScript.
* Avoid this by "noImplicitAny".
*/
const obj: any = { x: 0 }
const foo = () : number => 1
const bar = async () : Promise<string> => "string"
/**
* Object types.
*/
const printCoord = (pt: { x : number, y: number }) => { console.log(pt.x, pt.y) }
const printName = (obj : { first: string, last?: string }) => { console.log(obj.first, obj.last) } // Checking for optional arg is needed.
const printId = (id: number | string) => { console.log(id) } // Union type, need a runtime check with "typeof".
/**
* Type aliases - reusable types, cannot be changed after being created.
*/
type Point = {
x: number;
y: number;
}
type ID = number | string;
/**
* Interfaces - extendable way to name object types.
*/
interface Point {
x: number;
y: number;
}
interface Point { // Add more fields to the existing type.
z: number;
}
const pt : Point = { x: 1, y: 2, z: 3 }
/**
* Type assertions - similar to value (down/up) cast.
*/
// EventTarget <- Node <- Element <- HTMLElement <- HTMLCanvasElement.
const canvasEle = document.getElementById("canvas") as HTMLCanvasElement;
// Double assertion.
const resizeObserver = (window as unknown as Record<string, unknown>)['ResizeObserver']
/**
* Literal types.
*/
const printText = (s: string, alignment: "left" | "right" | "center") => {} // Combining literals into unions.
const compare = (a: string, b: string) : -1 | 0 | 1 => a === b ? 0 : a > b ? 1 : -1
declare function handleRequest(url: string, method: "GET" | "POST"): void
const reqA = { url: "https://hangyu.site", method: "GET" as "GET" } // Type assertion to always literal type.
const reqB = { url: "https://hangyu.site", method: "GET" } as const // Convert the entire object to be type literals.
handleRequest(reqA.url, reqA.method)
const liveDangerously = (x?: number | null) => { console.log(x!.toFixed()) } // Remove "null" and "undefined" from a specific type.
Type Narrowing
// "typeof" narrowing.
const padLeft = (padding: number | string, input: string) => {
if (typeof padding === "number") return " ".repeat(padding) + input
return padding + input
}
// Truthiness narrowing.
const printAll = (strs: string | string[] | null) => {
if (strs && typeof strs === "object") { // string[].
for (const s of strs) { console.log(s) }
} else if (typeof strs === "string") { // string.
console.log(strs)
}
}
// Type predicates - user-defined type guard.
const isFish = (pet: Fish | Bird): pet is Fish => { // The return type is a type predicate.
return (pet as Fish).swim !== undefined; // Narrowing the given argument to a specific type.
}
// Assertion functions.
interface Circle {
kind: 'circle'; // Literal type.
radius: number;
}
interface Square {
kind: 'square';
sideLength: number;
}
type Shape = Circle | Square; // A discriminated union, tags are recognized by their literal types.
const getArea = (shape: Shape) => {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2
case "square":
return shape.sideLength ** 2
default:
const _exhaustiveCheck: never = shape // "shape" is the type of "never" here.
return _exhaustiveCheck
}
}
More on Functions
type GreetFunction = (msg: string) => void // Function type.
const greeter: GreetFunction = (msg: string) => { console.log(msg) } // The parameter name in the type is required.
// Call signature in an object type.
type DescribableFn = {
description: string; // Other properties.
(arg: number): boolean;
}
const myFn: DescribableFn = (arg: number) => arg > 3
myFn.description = "fn description."
// Construct signature (for function call).
interface Obj { // The shape of Obj.
value: string;
id?: string;
}
class Name implements Obj { // One of the specific implementation.
value: string
constructor(s: string) { this.value = s }
}
type CallConstruct = {
new (s: string): Obj; // The return type should be stick to the shape.
}
((Ctor: CallConstruct) => new Ctor("Jason"))(Name)
// Generic functions (type parameters are for relating the types of multiple values).
function sum<Type>(arr: Type[]): number {
return arr.reduce((prev, num) => prev += parseInt(num as string), 0)
}
function longest<Type extends { length: number }>(a: Type, b: Type) { // Constrain the type parameters.
return a.length > b.length ? a : b
}
function combine<Type>(arrA: Type[], arrB: Type[]): Type[] {
return arrA.concat(arrB)
}
const arr = combine<string | number>([1, 2, 3], ["hello"])
function myForEach(arr: any[], callback: (arg: any, index: number) => void) {
for (let i = 0; i < arr.length; i++) {
callback(arr[i], i)
}
}
myForEach([1, 2, 3], (a) => console.log(a)) // Functions with fewer parameters (of the same types) can always take the place of functions with more parameters.
myForEach([1, 2, 3], (a, i) => console.log(a, i))
// Function overloads.
function makeDate(timestamp: number): Date // Overload signatures.
function makeDate(m: number, d: number, y: number): Date // Overload signatures.
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date { // Implementation signatures, which should be compatible with the overload signatures.
if (d !== undefined && y !== undefined) {
return new Date(y, mOrTimestamp, d)
} else {
return new Date(mOrTimestamp)
}
}
// "unknown" which is the type-safe counterpart of any.
// Anything is assignable to unknown, but unknown isn't assignable to anything -
// but itself and any without a type assertion or a control flow based narrowing. -
// Likewise, no operations are permitted on an unknown without first asserting or -
// narrowing to a more specific type.
function safeParse(s: string): unknown {
return JSON.parse(s)
}
function fail(msg: string): never {
throw new Error(msg)
}
function multiply(n: number, ...m: number[]) { // Rest parameter.
return m.map((x) => n * x)
}
const args = [8, 5] as const // TS doesn't assume that arrays are immutable, so using type literals here.
const angle = Math.atan2(...args)
// Parameter destructuring.
function sum({ x, y, z }: { x: number; y: number; z: number }) {
console.log(x + y + z)
}
type voidFuncType = () => void
const fn: voidFuncType = () => true // Valid, fn returns "more" information than type of "voidFunc".
const cbVoid = (cb: voidFuncType) => cb()
cbVoid(() => true) // Valid, same as above.
const literalFn = function(): void { // For literal void function, it must not return anything.
// @ts-expect-error
return true
}
Object Types
interface Type {
readonly prop: string // Readonly properties.
}
interface StrObjType {
[index: string]: string | undefined // When a StringArray is indexed with a string, it may return a string.
field: string
optFiled?: string
}
const strObj: StrObjType = { field: "hello", }
// Without the index signature, object literal may only specify known properties.
interface SquareConfig {
// [propName: string]: any;
color?: string
width?: number
}
const square: SquareConfig = { color: "red", width: 100, }
// Extending types.
interface BasicAddr {
name?: string
street: string
city: string
country: string
postalCode: string
}
interface AddressWithUnit extends BasicAddr {
unit: string
}
// Interaction types.
interface Colorful {
color: string
}
interface Circle {
radius: number
}
type ColorfulCircle = Colorful & Circle
const circle: ColorfulCircle = { color: "blue", radius: 42 }
// Generic object types.
interface Box<Type> {
contents: Type
}
// type Box<Type> = { contents: Type }
const box: Box<string> = { contents: "box" }
type NumBox = Box<number> // Reuse generic types.
type OrNull<Type> = Type | null
type OneOrMany<Type> = Type | Type[]
type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>
type OneOrManyOrNullStrings = OneOrManyOrNull<string>
// The "ReadonlyArray" type.
// Assignability isn’t bidirectional between regular Arrays and ReadonlyArrays.
function doStuff(values: ReadonlyArray<string>) {} // "values" cannot be changed.
// function doStuff(values: readonly string[]) {}
// Tuple types - another sort of Array type that knows exactly -
// how many elements it contains, and exactly which types it contains at specific positions.
type StringNumberPair = [string, number]
type Either2dOr3d = [number, number, number?] // Optional tuple elements can only come at the end.
type StringNumberBooleans = [string, number, ...boolean[]] // Tuple with rest elements.
function readButtonInput(...args: StringNumberBooleans) {
const [name, version, ...input] = args
}
// Read-only tupl - tuples tend to be created and left un-modified in most code.
const point = [3, 4] as const
function distanceFromOrigin([x, y]: readonly [number, number]) {
return Math.sqrt(x ** 2 + y ** 2)
}
distanceFromOrigin(point)
Type Manipulation
// Generic call signature.
interface GenericIdentityFn {
<Type>(arg: Type): Type
}
function identity<Type>(arg: Type): Type { return arg }
const myIdentity: GenericIdentityFn = identity
// Generic constraints.
interface Lengthwise {
length: number
}
function loggingIdentity<Type extends Lengthwise>(arg: Type) { return arg.length }
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) { return obj[key] }
function create<Type>(c: { new (): Type }): Type { return new c() } // Generic consturct signature.
// Generic with default types.
type Container<T, U> = {
element: T
children: U
}
declare function create<T extends HTMLElement = HTMLDivElement, U = T[]>(
element?: T,
children?: U
): Container<T, U>
// "keyof" type operator.
type Point = { x: number, y: number }
type P = keyof Point // "x" | "y".
// "typeof" type operator - only available on identifiers.
function fn() {
return { x: 10, y: 3 }
}
type P = ReturnType<tyepof fn>
// ^? type P = { x: number; y: number; }
// Indexded access types.
type Person = { age: number, name: string, alive: boolean }
type Age = Person['age']
type I1 = Person['age' | 'name']
type I2 = Person[keyof Person]
const MyArray = [
{ name: 'Jason', age: 20 },
{ name: 'Bob', age: 20 }
]
type ArrayPerson = typeof MyArray[number]
type ArrayAge = typeof MyArray[number]['age'] // Index with type literal "age".
// Conditional types.
interface IdLabel {
id: number
}
interface NameLabel {
name: string
}
type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel
function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {}
// "infer" keyword.
type GetReturnType<Type> = Type extends (...args: never[]) => infer Return ? Return : never
// Distributive conditional types.
type ToArray<Type> = Type extends any ? Type[] : never
type StrArrOrNumArr = ToArray<string | number> // string[] | number[].
type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never
type ArrOfStrOrNum = ToArrayNonDist<string | number> // (string | number)[].
// Mapped types.
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean
}
type Features = {
darkMode: () => void
newUserProfile: () => void
}
// Take all the properties from "Features" and change their values to be a boolean.
type FeatureOptions = OptionsFlags<Features>
type CreateMutable<Type> = {
-readonly [Property in keyof Type]: Type[Property] // Remove "readonly".
}
type Concrete<Type> = {
[Property in keyof Type]-?: Type[Property] // Remove "optional".
}
// Key remapping.
type Getters<Type> = {
[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
}
// Filter out keys.
type RemoveKindField<Type> = {
[Property in keyof Type as Exclude<Property, "kind">]: Type[Property]
}
type EventConfig<Events extends { kind: string }> = {
[E in Events as E["kind"]]: (event: E) => void
}
type SquareEvent = { kind: "square", x: number, y: number }
type Config = EventConfig<SquareEvent>
type ExtractPII<Type> = {
[Property in keyof Type]: Type[Property] extends { pii: true } ? true : false
}
// Template literal types.
type World = "world"
type Greeting = `hello ${World}`
type EmailLocaleIDs = "welcome_email" | "email_heading"
type FooterLocaleIDs = "footer_title" | "footer_sendoff"
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`
type Lang = "en" | "ja" | "pt"
// For each interpolated position in the template literal, the unions are cross multiplied.
type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`
// Defining a new string based on information inside a type.
type PropEventSource<Type> = {
on<Key extends string & keyof Type>
(eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void): void
}
declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>
// Intrinsic string manipulation types.
type ShoutyGreeting = Uppercase<Greeting> // Converts each character in the string to the uppercase version.
type QuietGreeting = Lowercase<Greeting> // Converts each character in the string to the lowercase version.
type CapitalizeGreeting = Capitalize<Greeting> // Converts the string to the cpitalized version.
type UncapitalizeGreeting = Uncapitalize<Greeting> // Converts the string to the uncpitalized version.
Classes
class OKGreeter {
name!: string // Not initialized, but no error.
}
class Greeter {
readonly name: string = "world" // This field can only be changed within construtor.
}
// Class heritage.
interface Pingable {
pint(): void
}
class Sonar implements Pingable {
pint() {}
}
Modules
// ES module syntax.
// @filename: animal.ts
export type Cat = { breed: string }
export interface Dog {
breeds: string[]
}
// @filename: app.ts
import type { Cat, Dog } from './animal.js'
// import { type Cat, type Dog, ... } from './animal.js' // Inline type imports.
type Animals = Cat | Dog
评论 | Comments