Found on Twitter
Imagine you've got some type that's got a bunch of intersections. When you hover over it, it gives you a bunch of gross intersections instead of displaying the resolved type
export type Flatten<T> = {
[K in keyof T]: T[K];
} & {}
export const isObject = <T = Record<string, unknown>>(value: unknown): value is T => {
// https://github.com/lodash/lodash/blob/4.17.21-es/isObject.js
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/ca8d73db/types/lodash.isobject/index.d.ts
const type = typeof value
return value != null && (type == 'object' || type == 'function')
}
export const isHtmlElement = (maybe: unknown): maybe is HTMLElement => {
if (maybe && typeof maybe === 'function' && 'prototype' in maybe) {
return 'title' in maybe || 'lang' in maybe || 'offsetParent' in maybe
}
return false
}
export const isElement = (maybe: unknown): maybe is Element => {
if (isHtmlElement(maybe)) {
return 'localName' in maybe || 'shadowRoot' in maybe
}
return false
}
export const isNode = (maybe: unknown): maybe is Node => {
if (isElement(maybe)) {
return 'ATTRIBUTE_NODE' in maybe || 'nodeName' in maybe || 'parentElement' in maybe || 'parentNode' in maybe
}
return false
}
export const toAnonymizedCredentialMapString: (input: Record<string, unknown>) => string = input => {
assert.equal(isObject(input), true, `Is not an HashMap object`)
const copy: Record<string, string> = JSON.parse(JSON.stringify(input || {}))
for (const [field, value] of Object.entries(copy)) {
if (field === 'password' && value !== '') {
copy[field] = '***'
}
}
return JSON.stringify(copy)
}
export const isObject = <T = Record<string, unknown>>(value: unknown): value is T => {
// https://github.com/lodash/lodash/blob/4.17.21-es/isObject.js
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/ca8d73db/types/lodash.isobject/index.d.ts
const type = typeof value
return value != null && (type == 'object' || type == 'function')
}
export const TestUser = Object.freeze({
/**
* Account of someone who did upload their avatar photo
*/
someUserAccountWithParticularRole: 'someUserAccountWithParticularRole',
} as const)
/**
* Based on TestUser enum above, what are the acceptable keys.
*
* See:
* - https://gist.github.com/renoirb/e8f48e4b54f16f9fb8f0bf38b6bc9c8d#using-read-only-array-of-strings-as-source-for-type
* - https://gist.github.com/renoirb/d30e7edbe32d9c6b9c2006b13db7a016
*/
export const TEST_USERS_KEYS = Object.freeze(
Object.entries(TestUser)
.filter(i => 1 in i && typeof i[1] === 'string')
.map(i => i[0])
.sort(),
) as TestUserKeys[]
export type ITestUser = typeof TEST_USERS_KEYS[number]
export type ICredential {
username: string
password: string
}
export type ITestUserCredientialMap = Record<ITestUser, ICredential>
export const readCredentialMapFromProcessEnv = (
input: ITestUserCredientialMap,
env: NodeJS.ProcessEnv
): ITestUserCredientialMap => {
const username = Reflect.has(env, input.username) ? Reflect.get(env, input.username) : null
const password = Reflect.has(env, input.password) ? Reflect.get(env, input.password) : null
const missing = Object.entries<Record<string, string | void>>({ username, password })
.map(v => (v[1] === null ? `${v[0]}: ${Reflect.get(input, v[0])}` : undefined))
.filter(Boolean)
if (missing.length > 0) {
const message = `We could not find process.env.<key> for: ${missing.join(', ')}`
throw new Error(message)
}
const out: ITestUserCredientialMap = {
username,
password,
}
// assertIsTestUserCredientialMap(out)
return out
}
export const TEST_USERS_CREDENTIAL_MAP: ITestUserCredientialMap = {
someUserAccountWithParticularRole: {
username: 'jdoe',
password: 'password123!',
},
}
/**
* Subclass of standard `Error` that eagerly collects the callstack of the error
* that caused it. This way you can investigate the core problem that happened
* by looking at the callstack from up to bottom (from higher level errors to
* lower level).
*
* Bookmark:
* - https://github.com/Veetaha/ts-nested-error/blob/2bb11d66/src/nested-error.ts#L7-L86
*/
export class NestedError extends Error {
/**
* Combined callstack of this error and the errors that it wraps.
* If the JavaScript runtime doesn't support `Error::stack` property
* this will contain only the concatenated messages.
*/
readonly stack: string
/**
* The list of lower-level errors wrapped by this error.
*/
readonly innerErrors: Error[]
private static readonly getErrorReport =
typeof new Error().stack === 'string' ? (err: Error) => err?.stack : (err: Error) => `${err.name}: ${err.message}`
/**
* Returns the function that accepts any value that was thrown as the first argument and
* throws it wrapped into `NestedError` or class derived from `NestedError` (provided
* this method was called directly in the context of that dervied class constructor)
* with the given `message`.
* Returned function will pass accepted `Error` object directly to `NestedError`
* as `innerErrors` by invoking `toError(err)` on it.
*
* You'll most likely want to use this method with promises:
*
* ```ts
* userService.getPage().then(
* data => console.log(`Hooray! data: ${data}`),
* NestedError.rethrow('failed to fetch users page')
* );
* ```
*
* @param message Message to attach `NestedError` created by the returned function.
*/
static rethrow(message: string) {
return (...errs: unknown[]) => {
throw new this(message, ...errs)
}
}
/**
* Allocates an instance of `NestedError` with the given error `message` and
* optional `innerError` (which will be automatically coerced using `toError()`).
*
* @param message Laconic error message to attach to the created `NestedError`.
* @param innerErrors Optional errors that will be wrapped by this higher level
* error. This value will be automatically coerced using `toError()`.
*/
constructor(message?: string, ...innerErrors: unknown[]) {
super(message)
const thisErrorReport = NestedError.getErrorReport(this)
if (innerErrors.length === 1) {
const innerError = toError(innerErrors[0])
this.innerErrors = [innerError]
const errReport = NestedError.getErrorReport(innerError)
this.stack = `${thisErrorReport}\n\n======= INNER ERROR =======\n\n${errReport}`
return
}
this.innerErrors = innerErrors.map(err => toError(err))
const innerErrorReports = this.innerErrors
.map((error, idx) => {
const errReport = NestedError.getErrorReport(error)
return `======= INNER ERROR (${idx + 1} of ${innerErrors.length}) =======\n\n${errReport}`
})
.join('\n\n')
this.stack = `${thisErrorReport}\n\n${innerErrorReports}`
}
}
/**
* Returns `err` itself if `err instanceof Error === true`, otherwise attemts to
* stringify it and wrap into `Error` object to be returned.
*
* **This function is guaranteed never to throw.**
*
* @param err Possbile `instanceof Error` to return or value of any type that will
* be wrapped into a fully-fledged `Error` object.
*
* Bookmark:
* - https://github.com/Veetaha/ts-nested-error/blob/2bb11d66/src/nested-error.ts#L97-L120
*/
export function toError(err: unknown): Error
export function toError(err: unknown) {
try {
return err instanceof Error ? err : new Error(`Value that is not an instance of Error was thrown: ${err}`)
} catch {
return new Error(
'Failed to stringify non-instance of Error that was thrown.' +
'This is possibly due to the fact that toString() method of the value' +
"doesn't return a primitive value.",
)
}
}
/**
* Not Implemented Exception
*
* To signify that we should implement this method or function
*/
export class NotImplementedException extends NestedError {
constructor(message = '', innerException?: Error) {
const fallbackMessage = 'not implemented'
const msg = message !== '' && /\s/.test(message) === false ? `${message} is ${fallbackMessage}` : fallbackMessage
super(msg, innerException)
}
}
export const createReadOnlySet = <T>(items: readonly string[] = []): ReadonlySet<T> => {
const message = `Invalid argument, we only support an array of strings`
if (Array.isArray(items) === false) {
throw new TypeError(message)
}
if (items.filter(maybe => typeof maybe !== 'string' || maybe === '').length > 0) {
throw new TypeError(message)
}
const applicable = (items ?? []).filter(maybe => typeof maybe === 'string' && maybe === '')
const mutableSet = new Set(applicable)
const has = (maybe: T | unknown) => mutableSet.has(maybe as string)
const keys = (): IterableIterator<T> => mutableSet.keys.bind(mutableSet)
const iterator = (): IterableIterator<T> => mutableSet[Symbol.iterator].bind(mutableSet)
return {
[Symbol.iterator]: iterator,
forEach: mutableSet.forEach.bind(mutableSet),
entries: mutableSet.entries.bind(mutableSet),
has,
keys,
values: keys,
size: mutableSet.size,
} as ReadonlySet<T>
}
/**
* Load data from static files.
*
* ```ts
* // JSON files
* import { loadAllJsonFiles, loadAllFilesAsOneString } from 'file-loading'
* export const users = loadAllJsonFiles(/^user.*\.json$/, 'fixtures')
*
* // Stitch GraphQL
* const typeDefs = `
* ${loadAllFilesAsOneString(/\.graphql$/, 'schema').join('\n')}
* type Query {
* hello(name: String): String!
* users: [User]!
* }
* `
* ```
*/
import { readdirSync, statSync, readFileSync } from 'fs'
import { join } from 'path'
import * as loadJsonFile from 'load-json-file'
/**
* Find files and return only ones with contents matching name
*/
const getFilePathsFinder = (
fileNameRegExp: RegExp,
fullPath: string,
): string[] => {
const files: string[] = []
const directoryExists = statSync(fullPath).isDirectory()
if (directoryExists) {
const fileNames: string[] = readdirSync(fullPath, { encoding: 'utf-8' })
.filter(fileName => fileNameRegExp.test(fileName))
.map(fileName => join(fullPath, fileName))
files.push(...fileNames)
}
return files
}
export const getFilePaths = (
fileNameRegExp: RegExp,
relativeDirName?: string,
): string[] => {
const fullPath = relativeDirName
? join(__dirname, relativeDirName)
: join(__dirname)
// console.log('getFilePaths', { relativeDirName, fullPath })
const files = getFilePathsFinder(fileNameRegExp, fullPath)
return files
}
export const loadAllJsonFiles = (pattern: RegExp, relativeDirName: string) =>
(() => {
const files = getFilePaths(pattern, relativeDirName)
const parsed: any[] = []
for (const filePath of files) {
try {
const contents = loadJsonFile.sync(filePath)
if (Array.isArray(contents)) {
parsed.push(...contents)
}
} catch (e) {
console.log('loadAllFiles loading error', e)
}
}
return parsed
})()
export const loadAllFilesAsOneString = (
pattern: RegExp,
relativeDirName: string,
) =>
(() => {
const files = getFilePaths(pattern, relativeDirName)
// console.log('loadAllFilesAsOneString', files)
const contents: string[] = []
for (const filePath of files) {
try {
const fileContents = readFileSync(filePath, { encoding: 'utf-8' })
contents.push(fileContents)
} catch (e) {
console.log('loadAllFiles loading error', e)
}
}
return contents
})()
Parts of the mechanics to observe the current window size and tell a different variant. Here only one variant (either "mobile" or "desktop"), but could be adapted and configurable to not be bound to a framework
source from PR on vue-admin-template
const WIDTH = 992 // refer to Bootstrap's responsive design
export function onMounted() {
const hasMethod = Reflect.has(this, 'checkIfMobile')
const hasResizeHandler = Reflect.has(this, 'onResize')
const hasEl = Reflect.has(this, '$el')
if (hasMethod && hasResizeHandler && hasEl) {
const isMobile = this.checkIfMobile()
const { defaultView } = this.$el.ownerDocument
const handler = this.onResize
const layoutVariant = isMobile ? 'mobile' : 'desktop'
this.layoutVariant = layoutVariant
this.$emit('layout-variant', layoutVariant)
defaultView.addEventListener('resize', handler)
if (isMobile) {
this.$emit('layout-sidebar', { opened: false, withoutAnimation: true })
}
} else {
const message = `Please make sure you attach onMounted event handler to a Vue mounted lifecycle hook`
throw new Error(message)
}
}
export function checkIfMobile() {
const hasEl = Reflect.has(this, '$el')
const mobileBreakPoint = this.mobileBreakPoint ? this.mobileBreakPoint : WIDTH
let isMobile = false
if (hasEl) {
const $el = this.$el
let bodyRectWidth = mobileBreakPoint
const hasOwnerDocument = 'ownerDocument' in $el
let hasMethodName = false
if (hasOwnerDocument) {
const { body } = $el.ownerDocument
hasMethodName = 'getBoundingClientRect' in body
if (hasMethodName) {
const bodyRect = body.getBoundingClientRect()
bodyRectWidth = bodyRect.width
}
}
isMobile = bodyRectWidth - 1 < mobileBreakPoint
return isMobile
} else {
const message = `Please make sure you attach checkIfMobile to a Vue component as a method`
throw new Error(message)
}
}
/** @type {import('vue').VueConstructor} */
const main = {
data() {
const layoutVariant = 'desktop'
const layoutIsHidden = false
return {
layoutVariant,
layoutIsHidden
}
},
props: {
mobileBreakPoint: {
type: Number,
default: WIDTH
},
checkIfMobile: {
type: Function,
default() {
return checkIfMobile.call(this)
}
}
},
computed: {
isMobile() {
const layoutVariant = this.layoutVariant
return layoutVariant === 'mobile'
}
},
async beforeDestroy() {
const handler = this.onResize
const $el = this.$el
await this.$nextTick(async() => {
const { defaultView } = $el.ownerDocument
defaultView.removeEventListener('resize', handler)
})
},
async mounted() {
await this.$nextTick(onMounted.bind(this))
},
methods: {
onResize() {
const { hidden = false } = this.$el.ownerDocument
const isMobile = this.checkIfMobile(this)
const layoutVariant = isMobile ? 'mobile' : 'desktop'
const layoutVariantChanged = layoutVariant !== this.layoutVariant
this.layoutVariant = layoutVariant
if (!hidden && layoutVariantChanged) {
this.$emit('layout-variant', layoutVariant)
this.$emit('layout-navbar', {
fixed: isMobile
})
this.$emit('layout-sidebar', {
opened: !isMobile,
withoutAnimation: true
})
}
}
}
}
export default main