https://www.typescriptlang.org/docs/handbook/
yarn global add typescript
npm install -g typescript
npx tsx myfile.ts
Alt:
npx ts-node myfile.ts
ts-node
Compile:
tsc greeter.ts
Example:
types/global.d.ts
types/supabase.ts
yarn api with openapi-typescript:
"api": "eval $(grep '^NEXT_PUBLIC_SUPABASE_URL' .env.local) && eval $(grep '^NEXT_PUBLIC_SUPABASE_API_KEY' .env.local) && npx openapi-typescript ${NEXT_PUBLIC_SUPABASE_URL}/rest/v1/?apikey=${NEXT_PUBLIC_SUPABASE_API_KEY} --output types/supabase.ts",
type vs interface vs class:
Always interface, except for: tuples, unions, mapped types, function types, overloading functions.
See https://stackoverflow.com/a/65948871 and https://javascript.plainenglish.io/when-to-best-use-type-class-or-interface-in-typescript-73bf66de19e9
type Order = {
product: string
amount: number
}
interface Order {
product: string
amount: number
}
const myOrder = { product: 'apple', amount: 2 }
interface CartProps {
items: CartItem[];
price: number;
text: string;
handleRemoveCoupon?: (...args: any) => void;
currencySymbol: {
before: string | null;
after: string | null;
};
}
Type as Array:
type MyArrayInterface = [
number,
(startValue: number) => void
]
class Message {
sender: string;
text: string;
constructor(sender: string, text: string) {
this.sender = sender;
this.text = text;
}
}
Abstract classes:
abstract class GameObject {
ctx: Expo2DContext
constructor (ctx: Expo2DContext) {
this.ctx = ctx
}
abstract setup (): void
abstract update (time: number): void
abstract draw (frameNr: number): void
}
- AND/Combined:
Human & Customer - OR:
string | null
extends:
interface RentalInputs extends ProductVariant, Customer, Rental {}
type RentalInputs = ProductVariant & Customer & Rental
type VideoWithUser = Video & {
username?: string
user_image_url?: string
}
interface OrderCompleted extends Order {
completed?: boolean
}
interface RentalFieldsProps {
inputs: ProductVariant & Customer & Rental
}
export const AppContext = `React.createContext`<Partial<ContextProps>>({})
Pick:
type TodoInfo = Pick<Todo, 'completed' | 'createdAt'>
Omit:
type TodoInfo = Omit<Todo, 'completed' | 'createdAt'>
- boolean
- number / bigint
- string
- tuple
- enum
- Array:
Array<string>same asstring[].let list: number[] = [1, 2, 3] - object: see also
Record<string, string>. Not number, string, boolean, bigint, symbol, null, or undefined. - function:
(param: string) => void - symbol
- void
- any / unknown
- null / undefined
- never
- Date
enum Color { Red, Green, Blue }
let c: Color = Color.Green
enum LetterNumber {
A = 1,
B = 2,
C = 3
}
let n: LetterNumber = LetterNumber.B
enum EmailTemplate {
GenericEmail = 'd-de3300ebee',
UserInvitation = 'd-3a10e324'
}
let order: [string, number] = ['apple', 2]
Function type:
interface SearchFunction {
(source: string, subString: string): boolean
}
Defining a function:
const myAdd = function (x: number, y: number): number {
return x + y
}
const myAdd = (x: number, y: number): number => x + y
Function in interface:
interface MenuPopoverProps {
open: boolean;
onClose: (event: React.SyntheticEvent) => void;
anchorEl: HTMLFormElement;
}
Record<string, object>
Array<Record<string, object>>
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
type ThreeStringProps = { prop1: string, prop2: string, prop3: string }
interface IHash {
[details: string]: string[];
}
- https://www.typescriptlang.org/docs/handbook/dom-manipulation.html#an-exploration-into-the-htmlelement-type
- https://github.com/microsoft/TypeScript/blob/master/lib/lib.dom.d.ts
Collections:
HTMLAllCollectionHTMLCollectionHTMLCollectionBaseHTMLCollectionOfHTMLFormControlsCollectionHTMLOptionsCollection
Elements:
HTMLAnchorElementHTMLAppletElementHTMLAreaElementHTMLAudioElementHTMLBaseElementHTMLBaseFontElementHTMLBlockElementHTMLBodyElementHTMLBodyElementEventMapHTMLBRElementHTMLButtonElementHTMLCanvasElementHTMLDataElementHTMLDataListElementHTMLDetailsElementHTMLDialogElementHTMLDirectoryElementHTMLDivElementHTMLDListElementHTMLDocumentHTMLElementHTMLElementDeprecatedTagNameMapHTMLElementEventMapHTMLElementTagNameMapHTMLEmbedElementHTMLFieldSetElementHTMLFontElementHTMLFormElementHTMLFrameElementHTMLFrameSetElementHTMLFrameSetElementEventMapHTMLHeadElementHTMLHeadingElementHTMLHRElementHTMLHtmlElementHTMLHyperlinkElementUtilsHTMLIframeElementHTMLIFrameElementHTMLImageElementHTMLInputElementHTMLLabelElementHTMLLegendElementHTMLLIElementHTMLLinkElementHTMLMapElementHTMLMarqueeElementHTMLMarqueeElementEventMapHTMLMediaElementHTMLMediaElementEventMapHTMLMenuElementHTMLMetaElementHTMLMeterElementHTMLModElementHTMLObjectElementHTMLOListElementHTMLOptGroupElementHTMLOptionElementHTMLOrSVGElementHTMLOrSVGImageElementHTMLOrSVGScriptElementHTMLOutputElementHTMLParagraphElementHTMLParamElementHTMLPictureElementHTMLPreElementHTMLProgressElementHTMLQuoteElementHTMLScriptElementHTMLSelectElementHTMLSlotElementHTMLSourceElementHTMLSpanElementHTMLStyleElementHTMLTableCaptionElementHTMLTableCellElementHTMLTableColElementHTMLTableDataCellElementHTMLTableElementHTMLTableHeaderCellElementHTMLTableRowElementHTMLTableSectionElementHTMLTemplateElementHTMLTextAreaElementHTMLTimeElementHTMLTitleElementHTMLTrackElementHTMLUListElementHTMLUnknownElementHTMLVideoElement
typeof:type PersonType = typeof person, ornewObject as typeof myObjectinstanceof:target instanceof HTMLInputElementindexofkeyof:type VehicleKey = keyof typeof CONFIG.vehicles;
const elementRef = useRef() as `React.MutableRefObject`<HTMLButtonElement>
const elementRef = useRef<HTMLButtonElement>();
const stringObj = String(myObj)
// I’m sure 'obj' is not null or undefined:
let sampleVar = obj!.field
const { name, age }: { name: string, age: number } = personObject
With type-definition:
type Item = {
id: number;
name: string;
stock: {
month: number;
week: number;
day: number;
};
}
const response = {
items: [{
id: 1,
name: 'TV',
stock: {
month: 10,
week: 5,
day: 4
}
}]
};
response.items.map(
({ id, name, stock: { day: dayStock } }: Item) => dayStock
);
try {
} catch (error: unknown) {
const errorMessage = (error instanceof Error) ? error.message : 'Unknown error'
console.warn(errorMessage)
}
export function removeUndefinedProps<T>(obj: T): Partial<T> {
const result: Partial<T> = {}; // Create a new object to hold the result
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
// Check if the property exists in the original object
const value = obj[key];
if (value !== undefined) {
result[key] = value;
}
}
}
return result;
}
export function removeUndefinedOrNullProps<T>(obj: T): Partial<T> {
const result: Partial<T> = {}; // Create a new object to hold the result
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
// Check if the property exists in the original object
const value = obj[key];
if (value !== undefined && value !== null) {
result[key] = value;
}
}
}
return result;
}
export function removeUndefinedOrNullOrEmptyStringProps<T>(obj: T): Partial<T> {
const result: Partial<T> = {}; // Create a new object to hold the result
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
// Check if the property exists in the original object
const value = obj[key];
if (value !== undefined && value !== null && value !== '') {
result[key] = value;
}
}
}
return result;
}
export function isUndefinedOrNull<T>(obj: T | null | undefined): obj is null | undefined {
return obj === null || obj === undefined;
}
export function isUndefinedOrNullOrEmptyString<T>(obj: T | null | undefined | string): boolean {
return isUndefinedOrNull(obj) || obj === '';
}
import type { VercelRequest, VercelResponse } from '@vercel/node'
export default function (req: VercelRequest, res: VercelResponse): void {
const { name = 'World' } = req.query
const nameStr = Array.isArray(name) ? name[0] : name
// res.statusCode = 200;
res.setHeader('Content-Type', 'application/json')
res.json({ message: `Hello ${nameStr}!` })
}
interface MyComponentProps = {
name: string
value: number
}
// Variants on how to declare types:
const MyComponent: `React.FunctionComponent`<MyComponentProps> = ({ name, value }): `React.ReactElement` => ()
const MyComponent: `React.FunctionComponent` = ({ name, value }: MyComponentProps): `React.ReactElement` => ()
const MyComponent = ({} : MyComponentProps) => ()
const MyComponent = ({ name } : { name: string }) => ()
- component:
React.FunctionComponent(aliasReact.FC) - component return value:
React.ReactElement - element/children:
React.ReactNode(notJSX.Element) - event:
React.SyntheticEventReact.MouseEventHandler<HTMLImageElement>React.ChangeEvent: (event:React.SyntheticEvent) => void
- event.target:
HTMLInputElement
Custom HTMLElementEvent:
interface HTMLSimpleElementEvent {
target: {
name: string
type?: string
value?: string
checked?: boolean
}
}
event: React.FormEvent<HTMLInputElement> (form submit):
- https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forms_and_events/
- https://stackoverflow.com/a/42066698
Usage: event: HTMLElementEvent<HTMLTextAreaElement>
type HTMLElementEvent<T extends HTMLElement> = Event & {
target: T;
// probably you might want to add the currentTarget as well
// currentTarget: T;
}
useState:
const [productInfo, setProductInfo] = useState<ProductInfo>({ sku: '', name: ''})
You can create higher-order components that take a type argument and return a component configured for that type.
import React from 'react';
interface MyComponentProps<T> {
data: T;
render: (data: T) => JSX.Element;
}
const MyComponent = <T extends {}>({ data, render }: MyComponentProps<T>) => {
return <div>{render(data)}</div>;
};
export default MyComponent;
-
Define a Function that Takes a Type Argument:
function createMyComponent<T>() { type MyComponentProps = { data: T; // Other props if needed }; return function MyComponent (props: MyComponentProps) { // You can use props.data inside the component, and TypeScript will know it's of type T return ( <div> {/* Render props.data or other content here */} </div> ); }; }
-
Use the Function to Create Components for Specific Types:
const MyComponentForMyDataType = createMyComponent<MyDataType>(); <MyComponentForMyDataType data={myData} />
import React from 'react'
import type { GetStaticPropsContext, GetStaticPropsResult, GetStaticPathsContext, GetStaticPathsResult } from 'next'
import { ParsedUrlQuery } from 'querystring'
interface MyPageParams extends ParsedUrlQuery {
slug: string
}
interface MyPageProps {
title: string
slug?: string | null
}
function MyPage ({ title, slug }: MyPageProps): `React.ReactElement` {
return (
...
)
}
export default MyPage
export async function getStaticProps (context: GetStaticPropsContext<MyPageParams>): Promise<GetStaticPropsResult<MyPageProps>> {
const slug = context.params?.slug ?? null
return {
props: {
title: 'My page',
slug
},
revalidate: 30 * 60 // 30 minutes
}
}
export async function getStaticPaths (context: GetStaticPathsContext): Promise<GetStaticPathsResult<MyPageParams>> {
const locales = context.locales ?? ['en']
return {
paths: [
// { params: { slug: 'value' } }
],
fallback: true // false -> 404, true: Next.js tries to generate page
}
}
https://www.npmjs.com/package/@typescript-eslint/eslint-plugin https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/README.md
yarn add -D eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin
https://standardjs.com/#can-i-use-a-javascript-language-variant-like-flow-or-typescript
yarn add -D ts-standard