Last active
June 15, 2021 23:44
-
-
Save jahe/62d4389850c75f3ff393ce6665742de2 to your computer and use it in GitHub Desktop.
TypeScript Cheatsheet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Activate the family of strict options | |
// You can then selectively opt out with specific strict options set to false | |
--strict | |
// Properties in ES6 classes should be initialized | |
// | |
// It checks weather one of the following conditions evaluatest to true: | |
// * has a type that includes undefined | |
// * has an explicit initializer | |
// * is definitely assigned to in the constructor | |
// | |
// The --strictNullChecks flag must be set in order for this option to have any effect | |
--strictPropertyInitialization | |
// Definite Assignment Operator | |
// Use the "!" operator on class properties to indicate that we know that a property eventually gets initialized at runtime | |
// so that no explicit initialization is required (e.g. when using a dependency injection framework) | |
class Library { | |
titles!: string[] | |
} | |
// Use the JS "in" operator to infer one of two possible types (type guard) | |
function redirect(user: Admin | User) { | |
if ("role" in user) { | |
routeToAdminPage(user.role) | |
} else { | |
routeToHomePage(user.email) | |
} | |
} | |
// Use Descriminative Unions to distinguish between types in a switch statement based on an objects property value | |
class RemoveAllAction { | |
readonly type = "Remove All" | |
} | |
class RemoveOneAction { | |
readonly type = "Remove One" | |
constructor(public payload: number) {} | |
} | |
type Actions = RemoveAllAction | RemoveOneAction | |
function todoReducer( | |
action: Actions, | |
state | |
) { | |
switch (action.type) { | |
case "Remove All": { | |
// action is infered to be of type RemoveAllAction | |
} | |
case "Remove One": { | |
// action is infered to be of type RemoveOneAction | |
} | |
} | |
} | |
// Force a switch statement to include all possible cases with a default block with a variable of type never | |
switch (action.type) { | |
case "Remove All": { | |
// ... | |
} | |
default: { | |
const x: never = action // <- This causes a compile error since "Remove One" is not part of the switch statement | |
} | |
} | |
// Use the Mapped Type Modifies to make e.g. a readonly version of an interface | |
// by adding or removing type modifies (with "+" or "-" whereas "+" is optional) | |
interface IPet { | |
name: string | |
age: number | |
favoritePark?: string | |
} | |
type ReadonlyPet = { | |
readonly [K in keyof IPet]: IPet[K] | |
} | |
type ExplicitPlusReadonlyPet = { | |
+readonly [K in keyof IPet]: IPet[K] | |
} | |
type StringPet { | |
[K in keyof IPet]: IPet[K] | string | |
} | |
type NonOptionalPet { | |
[K in keyof IPet]-?: IPet[K] | |
} | |
// Types (or "Type Aliases") vs. Interfaces | |
// - Library authors should use Interfaces to let users extend them locally | |
// - Interfaces with the same name are getting merged - Type Aliases not | |
// - Type Aliases are better for aliasing a specifc function | |
// - Union Type Aliases can't be used for class or interface inheritance since they can be either one type or the other | |
// Extend/Change an Interface of a library locally | |
// TS merges the declaration of Interfaces with the same name | |
// Add typings.d.ts file in the same directory | |
interface JQuery { | |
hideChildren(): JQuery | |
} | |
// Use the "unknown" type e.g. for server responses which can do change | |
// You can't access anything on an "unknown" type | |
// For accessing it you have to apply flow type narrowing or casting to it | |
// to make sure that it has a specific structure and type | |
interface IDataService { | |
getData(): unknown | |
} | |
const service: IDataService | |
const response = service.getData() | |
response.toUpperCase() // -> Throws a compilation error | |
if (typeof response === 'string') { | |
response.toUpperCase() // -> Works (after making the check) | |
} | |
const castResponse = <string> response | |
castResponse.toUpperCase() // -> Works (after making the casting) | |
// User Defined Type Guard | |
function isComment(type: any): type is IComment { | |
return (<IComment> type).message !== undefined | |
} | |
// Object with enum values as keys | |
// Source: https://stackoverflow.com/questions/52700659/how-to-use-enum-as-index-key-type-in-typescript | |
enum Letter { | |
A = 'a', | |
B = 'b' | |
} | |
interface LetterToString { | |
[key in Letter]?: string | |
} | |
// Work with "strict" mode on | |
// Filter null values from an array | |
const myFoos = myFoosWithNull.filter((foo): foo is Foo => foo != null) | |
// OR: | |
function isPresent<T>(value: T): value is NonNullable<T> { | |
return value != null | |
} | |
const myFoos = myFoosWithNull.filter(isPresent) | |
// Prevent never[] error messages when using empty arrays with "as ..." keyword | |
return { | |
names: [] as string[] | |
} | |
// Define types for a npm package that has no TypeScript types | |
// For example 'markdown-table' - It exports a function by default that expects a 2-dimensional array as an argument and returns a Markdown representation as string | |
// 1. Create a folder for the types of that npm package: src/@types/markdown-table | |
// 2. Create a TypeScript declaration file named index.d.ts in that folder with the following content: | |
declare function markdownTable(rows: any[][]): string | |
declare namespace markdownTable {} | |
declare module 'markdown-table' { | |
export = markdownTable | |
} | |
// If you need Node.js types just add the following comment to the top of the declaration file | |
/// <reference types="node" /> | |
// Useful type for dictionary kind of objects where properties should be immutable | |
type Dictionary<T> = Partial<{ [key: string]: Readonly<T> }> | |
// Make some types globally available like the Dictionary | |
global.d.ts | |
// Enrich the window object with custom properties | |
// global.d.ts | |
interface Window { | |
customLink: string; | |
} | |
// Specify key and value of objects with Record<K, T> | |
const colorMap: Record<string, { color: string; hover: string }> = { | |
nextjs: { color: '#0A7B83E2', hover: '#09686dE2' }, | |
javascript: { color: '#F5B50FE2', hover: '#d69e0cE2' } | |
} | |
// Omit some properties | |
type Person = { | |
name: strin | |
age: number | |
salary: number | |
} | |
type PersonWithoutSalary = Omit<Person, 'salary', 'age'> | |
const personObj: PersonWithoutSalary = { | |
name: 'Foo' | |
} | |
// Pick some properties | |
type Person = { | |
name: string | |
age: number | |
salary: number | |
} | |
type PersonWithoutSalary = Pick<Person, 'salary'> | |
const personObj: PersonWithoutSalary = { | |
salary: 10000, | |
age: 19 // Type '{ age: number }' is not assignable to type 'Pick<Person, "salary">' | |
} | |
// Create a type based on an existing object | |
const Person = { | |
name: 'John', | |
age: '20' | |
} | |
type PersonType = typeof Person | |
const newPersonObj: PersonType = { | |
name: 'Smith', | |
age: '21' | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment