Skip to content

Instantly share code, notes, and snippets.

@AzrizHaziq
Last active March 31, 2023 19:33
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AzrizHaziq/71f2316b9fb1e92816caa2da825dd10c to your computer and use it in GitHub Desktop.
Save AzrizHaziq/71f2316b9fb1e92816caa2da825dd10c to your computer and use it in GitHub Desktop.
JS & TS
<!-- default ngContent (https://github.com/angular/angular/issues/12530) -->
<div #ref><ng-content></ng-content></div>
<span *ngIf="ref.childNodes.length == 0">
Display this if ng-content is empty!
</span>
<!-- ngTemplateOutletContext -->
// passing variables
<div *ngFor="let item of list; let i = index">
<ng-container
[ngTemplateOutlet]="item.status === 'Open' ? openItem : notOpenItem"
[ngTemplateOutletContext]="{ $implicit: index item: item }">
</ng-container>
</div>
<ng-template #openItem let-item="item" let-index>
<div>{{ item.status }}</div>
<p>This is open item</p>
</ng-template>
<ng-template #notOpenItem let-item="item">
<div>{{ item.status }}</div>
<p>This is non-open Status Item</p>
</ng-template>
// https://blog.openreplay.com/the-ultimate-guide-to-browser-side-storage
(async () => {
// Storage API support
if (!navigator.storage) return;
const storage = await navigator.storage.estimate();
console.log(`permitted: ${ storage.quota / 1024 } Kb`);
console.log(`used : ${ storage.usage / 1024 } Kb`);
console.log(`% used : ${ Math.round((storage.usage / storage.quota) * 100) }%`);
console.log(`remaining: ${ Math.floor((storage.quota - storage.usage) / 1024) } Kb`);
})();
///////////////////////////////////////
//// this
class Obj {
getThis = () => this
getThis2 () {
return this;
}
}
const obj2 = new Obj();
obj2.getThis3 = obj2.getThis.bind(obj2);
obj2.getThis4 = obj2.getThis2.bind(obj2);
const answers = [
1, obj2.getThis(),
2, obj2.getThis.call(a),
3, obj2.getThis2(),
4, obj2.getThis2.call(a),
5, obj2.getThis3(),
6, obj2.getThis3.call(a),
7, obj2.getThis4(),
8, obj2.getThis4.call(a),
];
const a = {
a: 'a'
};
const obj = {
getThis: () => this,
getThis2 () {
return this;
}
};
obj.getThis3 = obj.getThis.bind(obj);
obj.getThis4 = obj.getThis2.bind(obj);
const answers = [
1, obj.getThis(),
2, obj.getThis.call(a),
3, obj.getThis2(),
4, obj.getThis2.call(a),
5, obj.getThis3(),
6, obj.getThis3.call(a),
7, obj.getThis4(),
8, obj.getThis4.call(a),
];
//////////////
function _0x39426c(e) {
function t(e) {
if ("string" == typeof e)
return function(e) {}
.constructor("while (true) {}").apply("counter");
1 !== ("" + e / e).length || e % 20 == 0 ? function() {
return !0
}
.constructor("debugger").call("action") : function() {
return !1
}
.constructor("debugger").apply("stateObject"),
t(++e)
}
try {
if (e)
return t;
t(0)
} catch (e) {}
}
setInterval(function() {
_0x39426c()
}, 1000)
----------------
// https://mariusschulz.com/blog/assertion-functions-in-typescript
function assertNonNullish<TValue>(
value: TValue,
message: string
): asserts value is NonNullable<TValue> {
if (value === null || value === undefined) {
throw Error(message);
}
}
const root = document.getElementById("root");
root; // Type: HTMLElement | null
assertNonNullish(root, "Unable to find DOM element #root");
root; // Type: HTMLElement
root.addEventListener("click", e => {
/* ... */
});
// https://gomakethings.com/formatting-dates-and-times-with-vanilla-js/
// This will automatically use the user's browser language for formatting
let formatDate = now.toLocaleString(navigator.language, {
dateStyle: 'long',
timeStyle: 'short',
hour12: true
});
/***** Log current active element in devtools *****/
$('body').on('focusin', function() {
console.log(document.activeElement);
});
/***** Pretty print state in html *****/
<pre>{JSON.stringify(this.state, null, 2)}</pre>
/***** CSS Animation *****/
* { animation-duration: 10s !important; }
// CURRY
const curry = (
f, arr = []
) => (...args) => (
a => a.length === f.length ?
f(...a) :
curry(f, a)
)([...arr, ...args]);
// Curry function stolen from Professor Frisby's Mostly Adequate Guide
// curry :: ((a, b, ...) -> c) -> a -> b -> ... -> c
function curry(fn) {
const arity = fn.length;
return function $curry(...args) {
if (args.length < arity) {
return $curry.bind(null, ...args);
}
return fn.call(null, ...args);
};
}
// By Erric Elliot
const curry = fn => (...args1) => {
if (args1.length === fn.length) {
return fn(...args1);
}
return (...args2) => {
const args = [...args1, ...args2];
if (args.length >= fn.length) {
return fn(...args);
}
return curry(fn)(...args);
};
};
const trace = msg => x => (console.log(msg, x), x)
const trace = msg => x => (console.log(msg, x), x)
// COMPOSE
const compose = (...fns) => x => fns.reduceRight((y, f) => f(y), x);
// PIPE
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x); // or
const pipe = (...fns) => compose(...fns.reverse())
// TRANSDUCER
const tArrayConcat = (a, c) => a.concat(c) // next reducer
const tTap = msg => step => (a, c) => { console.log(msg, a, c); return step(a, c) } // perform side effect
const tMap = f => step => (a, c) => step(a, f(c)); // transformer
const tFilter = predicate => step => (a, c) => predicate(c) ? step(a, c) : a; // filtering
const isEven = n => n % 2 === 0;
const double = n => n * 2;
const doubleEvens = compose(
tTap('start'),
tFilter(isEven),
tTap('after all even'),
tMap(double)
)(tArrayConcat);
console.log([1, 2, 3, 4, 5, 6].reduce(doubleEvens, [])) // [4, 8, 12]
// Pattern matching
// https://kyleshevlin.com/pattern-matching
function areWeThereYet(milesToGo) {
switch (true) {
case milesToGo <= 1:
return "We're hereeeee!"
case milesToGo <= 10:
return 'Almost. Just a little further.'
case milesToGo <= 100:
return 'A little more than an hour.'
case milesToGo <= 250:
return 'Just a half day away.'
case isEcommerceItems(items):
case isCMSItems(items):
return prepareForCollectionList(items)
case isDirector(person):
return 'Hello Director.'
case isRicky(person):
return 'Still here Ricky?'
case isStudent(person):
return `Hey, ${person.name}.`
default:
return "We're not even close so stop asking!"
}
}
function delay(timeout) {
return new Promise(
(resolve) => {
const timeoutHandle =
setTimeout(() => {
clearTimeout(timeoutHandle);
resolve()
}, timeout);
});
}
// Promise pool
// https://betterprogramming.pub/5-javascript-promises-util-functions-to-spice-up-your-apps-665affca236c
function completePromisesInPool(
promiseFactories: () => Promise<any>,
maxPoolSize: number
) {
return new Promise((res) => {
let nextPromise = 0;
const runPromise = () => {
nextPromise++;
console.log({ nextPromise, promiseFactories})
if (nextPromise > promiseFactories.length) {
res();
return;
}
return promiseFactories[nextPromise - 1]()
.then(() => runPromise())
}
Array.from({ length: maxPoolSize })
.map(() => runPromise());
})
}
// promise sequence
function completeInSequence(promiseFactories: Promise[]) {
return promiseFactories.reduce(
(chain, promiseFactory) => chain.then(()=> promiseFactory()),
Promise.resolve()
);
}
// 2nd option for pool work
// https://stackoverflow.com/questions/40639432/what-is-the-best-way-to-limit-concurrency-when-using-es6s-promise-all/51020535#51020535
const sleep = t => new Promise(rs => setTimeout(rs, t))
async function doWork(iterator) {
for (let [index, item] of iterator) {
await sleep(1000)
console.log(index + ': ' + item)
}
}
const iterator = Array.from('abcdefghij').entries()
const workers = new Array(2).fill(iterator).map(doWork)
// ^--- starts two workers sharing the same iterator
Promise.allSettled(workers).then(() => console.log('done'))
------------------------------------
Partial apply
const partialApply = (fn, ...fixedArgs) => {
return function (...remainingArgs) {
return fn.apply(this, fixedArgs.concat(remainingArgs));
};
};
const add = (a, b) => a + b;
const add10 = partialApply(add, 10);
console.log(add10(5), add10(10));
------------------------------------
// https://kentcdodds.com/blog/listify-a-java-script-array
// unfortunately TypeScript doesn't have Intl.ListFormat yet 😢
// so we'll just add it ourselves:
type ListFormatOptions = {
type?: 'conjunction' | 'disjunction' | 'unit'
style?: 'long' | 'short' | 'narrow'
localeMatcher?: 'lookup' | 'best fit'
}
declare namespace Intl {
class ListFormat {
constructor(locale: string, options: ListFormatOptions)
public format: (items: Array<string>) => string
}
}
type ListifyOptions<ItemType> = {
type?: ListFormatOptions['type']
style?: ListFormatOptions['style']
stringify?: (item: ItemType) => string
}
function listify<ItemType>(
array: Array<ItemType>,
{
type = 'conjunction',
style = 'long',
stringify = (thing: {toString(): string}) => thing.toString(),
}: ListifyOptions<ItemType> = {},
) {
const stringified = array.map(item => stringify(item))
const formatter = new Intl.ListFormat('en', {style, type})
return formatter.format(stringified)
}
// Chainable
// https://blog.openreplay.com/forever-functional-chaining-calls-for-fluent-interfaces
const makeChainable = (obj) =>
new Proxy(obj, {
get(target, property, receiver) { /* 1 */
return typeof target[property] === "function" /* 2 */
? (...args) => { /* 3 */
const result = target[property](...args); /* 4 */
return result === undefined ? receiver : result; /* 5 */
}
: target[property]; /* 6 */
}
});
class Mobster {
constructor(firstName, lastName, nickname) {
this.firstName = firstName;
this.lastName = lastName;
this.nickname = nickname;
}
setFirstName(newFirst) {
this.firstName = newFirst;
}
setLastName(newLast) {
this.lastName = newLast;
}
setNickname(newNickname) {
this.nickname = newNickname;
}
getFullName() {
return `${this.firstName} "${this.nickname}" ${this.lastName}`;
}
}
const makeMobster = (...args) => makeChainable(new Mobster(...args));
const gangster = makeMobster("Alphonse", "Capone", "Al");
console.log(gangster.getFullName());
// Alphonse "Al" Capone
console.log(
gangster
.setFirstName("Benjamin")
.setLastName("Siegel")
.setNickname("Bugsy")
.getFullName()
);
// Benjamin "Bugsy" Siegel
// https://www.chrisarmstrong.dev/posts/retry-timeout-and-cancel-with-fetch
const fetchWithRetries = async (
url,
options,
retryCount = 0,
) => {
// split out the maxRetries option from the remaining
// options (with a default of 3 retries)
const { maxRetries = 3, ...remainingOptions } = options;
try {
return await fetch(url, remainingOptions);
} catch (error) {
// if the retryCount has not been exceeded, call again
if (retryCount < maxRetries) {
return fetchWithRetries(url, options, retryCount + 1);
}
// max retries exceeded
throw error;
}
}
////////////////////////
// Create a promise that rejects after
// `timeout` milliseconds
const throwOnTimeout = (timeout) =>
new Promise((_, reject) =>
setTimeout(() =>
reject(new Error("Timeout")),
timeout
),
);
const fetchWithTimeout = (
url,
options = {},
) => {
const { timeout, ...remainingOptions } = options;
// if the timeout option is specified, race the
// fetch call
if (timeout) {
return Promise.race([
fetch(url, remainingOptions),
throwOnTimeout(timeout),
]);
}
return fetch(url, remainingOptions);
}
///////////////////////////////////?
const fetchWithCancel = (url, options = {}) => {
const controller = new AbortController();
const call = fetch(
url,
{ ...options, signal: controller.signal },
);
const cancel = () => controller.abort();
return [call, cancel];
};
// We don't await this call, just capture the promise
const [promise, cancel] = fetchWithCancel(
'https://cataas.com/cat?json=true',
);
// await the promise to get the response
const response = await promise;
// ...
// cancel the request (e.g. if we have rendered
// something else)
cancel();
// kentcdodds.com/blog/application-state-management-with-react
// context-provider-pattern.js
// src/count/count-context.js
import * as React from 'react'
const CountContext = React.createContext()
function useCount() {
const context = React.useContext(CountContext)
if (!context) {
throw new Error(`useCount must be used within a CountProvider`)
}
return context
}
function CountProvider(props) {
const [count, setCount] = React.useState(0)
const value = React.useMemo(() => [count, setCount], [count])
return <CountContext.Provider value={value} {...props} />
}
export {CountProvider, useCount}
// src/count/page.js
import * as React from 'react'
import {CountProvider, useCount} from './count-context'
function Counter() {
const [count, setCount] = useCount()
const increment = () => setCount(c => c + 1)
return <button onClick={increment}>{count}</button>
}
function CountDisplay() {
const [count] = useCount()
return <div>The current counter count is {count}</div>
}
function CountPage() {
return (
<div>
<CountProvider>
<CountDisplay />
<Counter />
</CountProvider>
</div>
)
}
//////////////////////////////////////////////////////////////////
// context-reducer-pattern.ts
// https://kentcdodds.com/blog/how-to-use-react-context-effectively
// src/count-context.js
import * as React from 'react'
type Action = {type: 'increment'} | {type: 'decrement'}
type Dispatch = (action: Action) => void
type State = {count: number}
type CountProviderProps = {children: React.ReactNode}
const CountStateContext = React.createContext<State | undefined>(undefined)
const CountDispatchContext = React.createContext<Dispatch | undefined>(
undefined,
)
function countReducer(state: State, action: Action) {
switch (action.type) {
case 'increment': {
return {count: state.count + 1}
}
case 'decrement': {
return {count: state.count - 1}
}
default: {
throw new Error(`Unhandled action type: ${action.type}`)
}
}
}
function CountProvider({children}: CountProviderProps) {
const [state, dispatch] = React.useReducer(countReducer, {count: 0})
return (
<CountStateContext.Provider value={state}>
<CountDispatchContext.Provider value={dispatch}>
{children}
</CountDispatchContext.Provider>
</CountStateContext.Provider>
)
}
function useCountState() {
const context = React.useContext(CountStateContext)
if (context === undefined) {
throw new Error('useCountState must be used within a CountProvider')
}
return context
}
function useCountDispatch() {
const context = React.useContext(CountDispatchContext)
if (context === undefined) {
throw new Error('useCountDispatch must be used within a CountProvider')
}
return context
}
function useCount() {
return [useCountState(), useCountDispatch()]
}
export {CountProvider, useCountState, useCountDispatch, useCount}
/////////////////////////
// Event in React
// https://epicreact.dev/how-to-type-a-react-form-on-submit-handler/
import * as React from 'react'
interface FormElements extends HTMLFormControlsCollection {
usernameInput: HTMLInputElement
}
interface UsernameFormElement extends HTMLFormElement {
readonly elements: FormElements
}
function UsernameForm({ onSubmitUsername }: { onSubmitUsername: (username: string) => void }) {
function handleSubmit(event: React.FormEvent<UsernameFormElement>) {
event.preventDefault()
onSubmitUsername(event.currentTarget.elements.usernameInput.value)
}
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="usernameInput">Username:</label>
<input id="usernameInput" type="text" />
</div>
<button type="submit">Submit</button>
</form>
)
}
/////////////////////////////////////
// kentcdodds.com/blog/application-state-management-with-react
function countReducer(state, action) {
switch (action.type) {
case 'INCREMENT': {
return {count: state.count + 1}
}
default: {
throw new Error(`Unsupported action type: ${action.type}`)
}
}
}
function CountProvider(props) {
const [state, dispatch] = React.useReducer(countReducer, {count: 0})
const value = React.useMemo(() => [state, dispatch], [state])
return <CountContext.Provider value={value} {...props} />
}
function useCount() {
const context = React.useContext(CountContext)
if (!context) {
throw new Error(`useCount must be used within a CountProvider`)
}
const [state, dispatch] = context
const increment = () => dispatch({type: 'INCREMENT'})
return {
state,
dispatch,
increment,
}
}
// https://betterprogramming.pub/rxjs-best-practices-7f559d811514
import { TestScheduler } from "rxjs/testing";
import { filter, map } from "rxjs/operators";
describe("Awesome testing with Marble Diagrams", () => {
const scheduler = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
it("should filter non-Water type pokemon and add attack property", () => {
scheduler.run(({ cold, expectObservable }) => {
const values = {
a: { name: "Bulbasur", type: "Grass" },
b: { name: "Charmander", type: "Fire" },
c: { name: "Squirtle", type: "Water" }
};
const marbleDiagram = "-a-b-c|";
const pokemon$ = cold(marbleDiagram, values);
const expectedMarbleDiagram = "-----c|";
const expectedValues = {
c: { name: "Squirtle", type: "Water", attack: 30 }
};
const result = pokemon$.pipe(
filter(({ type }) => type === "Water"),
map(pokemon => ({ ...pokemon, attack: 30 }))
);
expectObservable(result).toBe(expectedMarbleDiagram, expectedValues);
});
});
});
---------------------------------------------
import { TestScheduler } from "rxjs/testing";
import { Observable } from "rxjs";
import { filter } from "rxjs/operators";
describe("Awesome testing with Marble Diagrams", () => {
const scheduler = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
const isMultipleOfTen = (number: number) => number % 10 === 0;
it("should filter numbers that aren't multiples of ten", () => {
scheduler.run(({ cold, expectObservable }) => {
const values = {
a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10
};
const number$ = cold("-a-b-c-d-e-f-g-h-i-j|", values);
const expectedMarbleDiagram = "-------------------a|";
const expectedValues = { a: 10 };
const result = number$.pipe(filter(number => isMultipleOfTen(number)));
expectObservable(result).toBe(expectedMarbleDiagram, expectedValues);
});
});
});
//////////////////////// EXTENDS ///////////////////////
interface Array<T> {
getBy<P extends keyof T>(prop: P, value: T[P]): T | null;
}
type Student = {
name: string;
age: number;
hasScar: boolean;
};
type Teacher = {
name: string;
subject: string;
};
Array.prototype.getBy = function <T, P extends keyof T>(
this: T[],
prop: P,
value: T[P]
): T | null {
return this.filter(item => item[prop] === value)[0] || null;
};
const students: Student[] = [
{ name: "Harry", age: 17, hasScar: true },
{ name: "Ron", age: 17, hasScar: false },
{ name: "Hermione", age: 16, hasScar: false }
];
const teachers: Teacher[] = [
{ name: "Snaip", subject: "Potions" },
{ name: "McGonagall", subject: "Transfiguration" }
];
const bestie = students.getBy("name", "Ron");
const potionsTeacher = teachers.getBy("subject", "Potions")
type Student = {
name: string;
age: number;
hasScar: boolean;
};
type Teacher = {
name: string;
subject: string
}
const students: Student[] = [
{ name: "Harry", age: 17, hasScar: true },
{ name: "Ron", age: 17, hasScar: false },
{ name: "Hermione", age: 16, hasScar: false }
];
const teachers: Teacher[] = [
{ name: "Snaip", subject: "Potions" },
{ name: "McGonagall", subject: "Transfiguration" }
]
function getBy<T, P extends keyof T>(model: T[], prop: P, value: T[P]): T | null {
return model.filter(item => item[prop] === value)[0] || null
}
const result = getBy(students, "naem", "Hermione")
// Error: Argument of type '"naem"' is not assignable to parameter of type '"name" | "age" | "hasScar"'.
const anotherResult = getBy(students, "hasScar", "true")
// Error: Argument of type '"true"' is not assignable to parameter of type 'boolean'.
const yetAnotherResult = getBy(students, "name", "Harry")
// That's cool, yetAnotherResult: Student
const lastResult = getBy(teachers, "subject", "Potions")
// Works for every type - lastResult: Teacher
//////////////////////// KEYOF ////////////////////////////////////////
interface Configuration {
baseUrl: string;
ttl: number;
}
type ConfigurationOption = keyof Configuration;
function set<T extends ConfigurationOption>(key: T, value: Configuration[T]) {
console.log(`Setting: ${key} to ${value}`);
}
set('baseUrl', 'https://moin.world');
set('ttl', 42);
set('baseUrl', true); // Invalid! Compiler catches this.
type ConfigurationLastUpdated = {
[O in ConfigurationOption]: Date
};
const updated: ConfigurationLastUpdated = {
baseUrl: new Date(),
ttl: new Date()
};
type Color = 'red' | 'blue' | 'green';
type ColorToRgb = { [C in Color]?: string };
const rgbMap: ColorToRgb = {
red: '#ff0000'
};
// Object is basically anything that is not undefined or null
const foo: Object = 'foo';
/////////////////////// NAMESPACE ///////////////////////////
/***** Namespaces *******/
namespace ApiModel {
export interface Order {
id: number;
total: number;
}
}
const abc: ApiModel.Order; = { id: 1, total: 20 }
////////////////////// PARTIAL //////////////////////////
/***** Partial *******/
type Partial<T> = {
[P in keyof T]?: T[P];
};
// using the interface but make all fields optional
import { Customer } from './api.model';
export class MyComponent {
cust: Partial<Customer>; /
ngOninit() {
this.cust = { name: 'jane' }; // no error throw because all fields are optional
}
}
/////////////////////// REDUX ////////////////////////////////////
const initialState: LoginState = {
username: '',
password: '',
isLoading: false,
error: '',
isLoggedIn: false,
variant: 'login',
};
interface LoginState {
username: string;
password: string;
isLoading: boolean;
error: string;
isLoggedIn: boolean;
variant: 'login' | 'forgetPassword';
}
type LoginAction =
| { type: 'abc', password: string, payload: { isLoading: boolean } }
| { type: 'login' | 'success' | 'error' | 'logOut' }
| { type: 'field'; fieldName: string; payload: boolean };
function loginReducer(state: LoginState, action: LoginAction): LoginState {
switch (action.type) {
case 'abc': {
return {
...state,
isLoading: action.payload.isLoading,
password : action.password
}
}
case 'field': {
return {
...state,
[action.fieldName]: action.payload,
};
}
case 'login': {
return {
...state,
error: '',
isLoading: true,
};
}
case 'success': {
return {
...state,
isLoggedIn: true,
isLoading: false,
};
}
case 'error': {
return {
...state,
error: 'Incorrect username or password!',
isLoggedIn: false,
isLoading: false,
username: '',
password: '',
};
}
case 'logOut': {
return {
...state,
isLoggedIn: false,
};
}
default:
return state;
}
}
/////////////// Array //////////////////
type Car = 'BMW' | 'MERC' | 'TOYOTA';
type Moto = 'KAWASAKI' | 'EX5' | 'HONDA';
const arr: Array<Car | Moto> = ['HONDA', 'BMW', 'MERC'];
const arr2: Car[] | Moto[] = ['KAWASAKI', 'EX5', 'HONDA'];
//////////////// TUPLE ///////////////////////
interface IQuery<TReturn, UParams extends any[] = []> {
(...args: UParams): Promise<TReturn>
}
type Params: [title: string, artist: string]
const findSongAlbum: IQuery<Album, Params> = (title, artist) => {
// data fetching code...
const albumName = '1989';
return Promise.resolve({
title: albumName
});
}
/////////////////// Conditional TS ////////////////////////////?
// https://icanmakethiswork.blogspot.com/2021/04/the-service-now-api-and-typescript_83.html
export type PropertyValue<
TAllTrueFalse extends DisplayValue,
TValue = string,
TDisplayValue = string
> = TAllTrueFalse extends 'all'
? ValueAndDisplayValue<TValue, TDisplayValue>
: TAllTrueFalse extends 'true'
? TDisplayValue
: TValue;
const reasonAll: PropertyValue<'all', string, string | null> = {
"display_value": null,
"value": ""
};
const reasonTrue: PropertyValue<'true', string, string | null> = null;
const reasonFalse: PropertyValue<'false', string, string | null> = '';
//////////////////////////////////////////
// https://kentcdodds.com/blog/typescript-function-syntaxes?ck_subscriber_id=539300779
//////////////////////////////////////////
// Simple type for a function, use =>
type FnType = (arg: ArgType) => ReturnType
// Every other time, use :
type FnAsObjType = {
(arg: ArgType): ReturnType
}
interface InterfaceWithFn {
fn(arg: ArgType): ReturnType
}
const fnImplementation = (arg: ArgType): ReturnType => {
/* implementation */
}
//////////////////////////////////////////////
declare function MathFn(a: number, b: number): number
declare namespace MathFn {
let operator: '+'
}
const sum: typeof MathFn = (a, b) => a + b
sum.operator = '+'
///////////////////////////////////////////
interface MathUtilsInterface {
sum(a: number, b: number): number
}
class MathUtils implements MathUtilsInterface {
sum(a: number, b: number): number {
return a + b
}
}
const math = new MathUtils()
const sum = math.sum
sum(1, 2)
///////////////////////////////////
declare const sum: {
(a: number, b: number): number
operator: string
}
export default sum
const sum = (a: number, b: number): number => a + b
sum.operator = '+'
//////////////////////////////////
type asyncSumCb = (result: number) => void
// define all valid function signatures
function asyncSum(a: number, b: number): Promise<number>
function asyncSum(a: number, b: number, cb: asyncSumCb): void
// define the actual implementation
// notice cb is optional
// also notice that the return type is inferred, but it could be specified
// as `void | Promise<number>`
function asyncSum(a: number, b: number, cb?: asyncSumCb) {
const result = a + b
if (cb) return cb(result)
else return Promise.resolve(result)
}
/////////////////////////////////?
function* generator(start: number) {
const newStart: number = yield start + 1
yield newStart + 2
}
var iterator = generator(0)
console.log(iterator.next()) // { value: 1, done: false }
console.log(iterator.next(3)) // { value: 5, done: false }
console.log(iterator.next())
///////////////////////////////////
// React
function arrayify2<Type>(a: Type): Array<Type> {
return [a]
}
// use extends unknown because < is JSX or generic?
const arrayify = <Type extends unknown>(a: Type): Array<Type> => [a]
////////////////////////////////////
// Tuples and types with interfaces
interface FixedLengthArray<T, U, L extends number> extends Array<T| U> {
0: T
1: U
length: L
}
type Foo = FixedLengthArray<number, boolean, 6>
const foo1: Foo = [1, true, 3, 4, 5, false];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment