Skip to content

Instantly share code, notes, and snippets.

View temoncher's full-sized avatar
🐢

Artem Baranov temoncher

🐢
View GitHub Profile
@temoncher
temoncher / ZodForm.tsx
Created January 6, 2023 22:08
A more typesafe alternative to `react-hook-form`, useZodForm
import { useState } from 'react';
import { z } from 'zod';
import reactLogo from './assets/react.svg';
import './App.css';
import { useZodForm, register } from './useZodForm';
type ObjectKey = keyof any;
type Field<N extends ObjectKey, V> = {
name: N;
@temoncher
temoncher / noUnusedExports.js
Created December 24, 2022 15:43
ESLint rule to remove unused exports based on `ts-prune`
// @ts-check
const { execSync } = require('child_process');
const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils');
/** @type {Record<string, { exportName: string, usedInModule: boolean }[]>} */
let cache;
const loadUnusedExports = () => {
if (!cache) {
@temoncher
temoncher / deleteUnusedFiles.js
Created December 24, 2022 12:37
Script to delete unused files based on `ts-prune`
// @ts-check
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const { parse } = require('@typescript-eslint/parser');
const eslintRc = require('../.eslintrc.js');
const unusedExportsByFilename = execSync('ts-prune')
.toString()
@temoncher
temoncher / preferObjectStyleEnums.js
Last active December 17, 2022 10:43
ESLint rule that autofixes enums into constants
// @ts-check
/** @type {import('@typescript-eslint/utils').TSESLint.RuleModule} */
module.exports = {
meta: {
docs: {
description:
"See [TypeScript Enums are TERRIBLE. Here's Why. - Michigan TypeScript](https://www.youtube.com/watch?v=0fTdCSH_QEU)",
},
type: 'suggestion',
fixable: 'code',
@temoncher
temoncher / isDefined.ts
Created December 1, 2022 13:16
`isUndefined`, `isDefined` and `complement` typeguards
function complement<A, T extends A>(predicate: (arg: A) => arg is T): (arg: A) => arg is Exclude<A, T>;
function complement<A extends any[]>(predicate: (...args: A) => boolean) {
return (...args: A) => !predicate(...args);
}
function isUndefined<A>(anything: A | undefined): anything is undefined;
function isUndefined(anything: any): anything is undefined {
return anything === undefined
};
@temoncher
temoncher / includeComponentNameInPropTypeDefinition.js
Created November 10, 2022 13:21
ESLint rule to include component name in a prop type definition
const isCapitalized = (str) => str[0] === str[0].toUpperCase();
const reportPropsError = (componentName, node, context, sourceCode) => {
const newPropsName = `${componentName}Props`;
const allPropsIdentifierTokens = sourceCode.ast.tokens.filter(
(token) => token.type === 'Identifier' && token.value === 'Props'
);
context.report({
node,
@temoncher
temoncher / EmptyObject.ts
Created November 10, 2022 09:07
Soultion for `{}` type with `EmptyObject` global type
// global.d.ts
declare global {
// eslint-disable-next-line @typescript-eslint/ban-types
type EmptyObject = Record<string, never>;
}
// .eslintrc.js
'@typescript-eslint/ban-types': [
2,
{
@temoncher
temoncher / transfer-versions-from-package-lock.js
Created October 31, 2022 10:08
Transfer versions from package-lock.json to package.json
const fs = require('fs');
const packageLockJson = require('./package-lock.json');
const packageJson = require('./package.json');
const compareVersions = (ver1, ver2) => {
const v1 = ver1.split('.');
const v2 = ver2.split('.');
return v1.some((v, i) => Number(v) > Number(v2[i]));
@temoncher
temoncher / stringIncludes.ts
Last active October 27, 2022 11:10
Typed String.prototype.includes
type FilterUnion<T extends string, Keys extends string> = {
[Key in Keys extends infer R ? (R extends `${string}${T}${string}` ? R : never) : never]: string;
} extends infer O
? keyof O
: never;
export const stringIncludes = <T extends string>(text: T) => <S extends string>(
str: S
): str is FilterUnion<T, S> => str.includes(text);
@temoncher
temoncher / updatePackageJson.js
Created September 7, 2022 12:55
Update `package.json` package versions with `package-lock.json` versions
const fs = require('fs');
const { mapValues } = require('lodash');
const lock = require('./package-lock.json');
const pack = require('./package.json');
const newDependencies = mapValues(
pack.dependencies,
(depVersion, depName) => lock.dependencies[depName].version
);
const newDevDepencies = mapValues(