Skip to content

Instantly share code, notes, and snippets.

@VladSez
Last active September 15, 2022 11:05
Show Gist options
  • Save VladSez/e0a9b430cbee5fb33ddc475188d1be1d to your computer and use it in GitHub Desktop.
Save VladSez/e0a9b430cbee5fb33ddc475188d1be1d to your computer and use it in GitHub Desktop.
Some TS snippets
// challenges: https://github.com/type-challenges/type-challenges
// more challenges https://github.com/LearningTypeScript/projects/tree/main/projects/type-operations/array-type-shenanigans
// ts-play url shortener
// https://tsplay-dev.vercel.app/
// https://tsplay.dev/wepV1W
// https://github.com/type-challenges/type-challenges/blob/main/questions/00949-medium-anyof/README.md
// https://tsplay.dev/NDyplm
// https://github.com/type-challenges/type-challenges/blob/main/questions/00298-medium-length-of-string/README.md
// https://tsplay.dev/wEP3ZW
// https://github.com/type-challenges/type-challenges/blob/main/questions/02688-medium-startswith/README.md
// https://tsplay.dev/mM4erN
// https://github.com/type-challenges/type-challenges/blob/main/questions/02693-medium-endswith/README.md
// https://tsplay.dev/w6PJ0m
// https://github.com/type-challenges/type-challenges/blob/main/questions/02070-medium-drop-char/README.md
// https://tsplay.dev/WPRYEN
// https://github.com/type-challenges/type-challenges/blob/main/questions/05153-medium-indexof/README.md
// https://tsplay.dev/w2Py1m
// https://github.com/type-challenges/type-challenges/blob/main/questions/00898-easy-includes/README.md
// https://tsplay.dev/Wk0JPw
// https://github.com/type-challenges/type-challenges/blob/main/questions/00612-medium-kebabcase/README.md
// https://tsplay.dev/WJyOZm
// object key to camel case, splitBy, concat
// https://tsplay.dev/Wz5Kem
// https://github.com/type-challenges/type-challenges/blob/main/questions/00008-medium-readonly-2/README.md
// https://tsplay.dev/mAK3Bw
// flatten
// https://tsplay.dev/WKyEyN
// https://github.com/type-challenges/type-challenges/blob/main/questions/05117-medium-without/README.md
// https://tsplay.dev/WJyE5m
// https://github.com/type-challenges/type-challenges/blob/main/questions/04425-medium-greater-than/README.md
// https://tsplay.dev/WPRVLN
// https://github.com/type-challenges/type-challenges/blob/main/questions/05360-medium-unique/README.md
// https://tsplay.dev/w2Pnzm
// https://github.com/type-challenges/type-challenges/blob/main/questions/00527-medium-append-to-object/README.md
// https://tsplay.dev/wjngMw
// https://github.com/type-challenges/type-challenges/blob/main/questions/04037-hard-ispalindrome/README.md
// https://tsplay.dev/mZ40ow
// https://github.com/type-challenges/type-challenges/blob/main/questions/05310-medium-join/README.md
// https://tsplay.dev/NdA1nm
// https://github.com/type-challenges/type-challenges/blob/main/questions/00011-easy-tuple-to-object/README.md
// https://tsplay.dev/w2PMzm
// https://github.com/type-challenges/type-challenges/blob/main/questions/02822-hard-split/README.md
// https://tsplay.dev/mbB4Em
// https://github.com/type-challenges/type-challenges/blob/main/questions/00399-hard-tuple-filter/README.md
// https://tsplay.dev/m3PMEW
// https://github.com/type-challenges/type-challenges/blob/main/questions/00147-hard-c-printf-parser/README.md
// https://tsplay.dev/WoaXeN
// https://github.com/type-challenges/type-challenges/blob/main/questions/00531-medium-string-to-union/README.md
// https://tsplay.dev/NV4G5W
// component factory
// https://tsplay.dev/NlE75m
// input must be validated with Zod
// https://tsplay.dev/W4PvvW
// factory?
// https://tsplay.dev/w8L7Pw
type Count<Input extends number, TotalCount extends 0[] = []> = TotalCount['length'] extends Input
? TotalCount : Count<Input, [ ...TotalCount, 0]>
type Inc<I extends number> = [...Count<I>, 0]['length']
type test = Count<5>['length'];
// ^?
type Test2 = Inc<4>
// ^?
type Length<Input extends unknown[], TotalCount extends 0[] = []> = TotalCount['length'] extends Input['length']
? TotalCount['length'] : Length<Input, [ ...TotalCount, 0]>
type Length2<T extends readonly any[]> = T['length']
type teslaLength = Length2<['tesla', 'model 3', 'model X', 'model Y']>
// ^?
type InvertBits<I, R extends string = ''> = I extends `${infer Head}${infer Tail}`
? InvertBits<Tail, `${R}${Head extends '1' ? '0' : '1'}`>
: R;
type kek = InvertBits<'001'>
// ^?
type Head<I extends string> = I extends `${infer Head}${infer Tail}` ? `${Head}` : void;
type firstLetter = Head<'Vlad'>
// ^?
type Includes<Input extends unknown[], Search extends string|number|null|undefined, Count extends 0[] = []> =
Search extends Input[Count['length']]
// found
? true
// array is iterated
: Input['length'] extends Count['length']
// not found
? false
// iterate
: Includes<Input, Search, [...Count, 0]>
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', ''], 'Kars'> // expected to be `true`
// ^?
type isPillarMen2 = Includes<[12,3,4,null], 232323> // expected to be `false`
// ^?
type Push<Input extends unknown[], Value extends unknown> = Input extends [...infer rest] ? [...rest, Value] : never;
type Result = Push<[1, 2], 3> // [1, 2, 3]
// ^?
type Unshift<Input extends unknown[], Value extends unknown> = Input extends [...infer rest] ? [Value, ...rest] : never;
type Result2 = Unshift<[1, 2], 0> // [0, 1, 2,]
// ^?
const foo = (arg1: string, arg2: number): void => {}
type MyParameters<T extends Function> = T extends (...Args: infer A) => unknown ? A : never;
type FunctionParamsType = MyParameters<typeof foo> // [arg1: string, arg2: number]
// ^?
type Reverse<Input extends unknown[], Result extends unknown[] = []> =
Input extends [...infer Head, infer Tail]
? Reverse<Head, [...Result, Tail]>
: Result
type reversed = Reverse<['a','b', 'c']> // ['c', 'b', 'a']
// ^?
type Reverse2<T extends any[]> = T extends [infer First, ...infer Rest] ? [...Reverse<Rest>, First] : [];
type reversed2 = Reverse2<[1,2,3,4,5]> // ['c', 'b', 'a']
// ^?
type Empty = ' ' | '\n' | '\t'
type Trim<S extends string> = S extends `${Empty}${infer Rest}` | `${infer Rest}${Empty}` ? Trim<Rest> : S
type Trimmed = Trim<` lol `>
// ^?
type ReplaceAll<Input extends string, From extends string, To extends string> =
From extends ''
? Input
:
Input extends `${infer Head}${From}${infer Tail}`
? ReplaceAll<`${Head}${To}${Tail}`, From, To>
: Input
type replaced = ReplaceAll<'111', '1', '2'> // expected to be 'types'
// ^?
type KebabCase<
Input extends string,
Result extends string = '',
Count extends 0[] = [],
> = Input extends `${infer Head}${infer Rest}`
? Head extends Lowercase<Head>
? KebabCase<Rest, `${Result}${Head}`, [...Count, 0]>
: KebabCase<
Rest,
Count['length'] extends 0
? `${Result}${Lowercase<Head>}`
: `${Result}${`-${Lowercase<Head>}`}`,
[...Count, 0]
>
: Result;
type Kebabed = KebabCase<'FooBar'> // foo-bar
// ^?
type Character = '+' | '-'
type SplitBy<T extends string> = T extends `${infer U}${Character}${infer P}`? [U, ...SplitBy<P>] : [T];
type test2 = SplitBy<'lol+kek+ke23'> // ['lol', 'kek', 'ke23']
// ^?
type test3 = SplitBy<'lol-kek-ke23'> // ['lol', 'kek', 'ke23']
// ^?
interface RandomKeys {
snake_case: string;
PascalCase: boolean;
camelCase: number;
SCREAMING_SNAKE_CASE: bigint;
}
/**
* TS Question - how do you transform ⬆️ into ⬇️?
*/
interface CoercedKeys {
snakeCase: string;
pascalCase: boolean;
camelCase: number;
screamingSnakeCase: bigint;
}
type CH = '_' | '-'
type HasUnderscore<T extends string> = T extends `${string}_${string}` ? true: false;
type Concat<T extends string[], Res extends string = ''> = T extends [infer U extends string, ...infer Rest extends string[]] ? Concat<Rest, `${Res}${Capitalize<U>}`>: Res;
type SplitUnderscore<T extends string> = T extends `${infer U}${CH}${infer P}`? [U, ...SplitUnderscore<P>] : [T]
type MaybeLowercase<T extends string> = T extends `${string}${CH}${string}` ? Lowercase<T> : T extends Uppercase<T> ? Lowercase<T> : T;
type Result<Input extends {}> = {[k in keyof Input as Uncapitalize<Concat<SplitUnderscore<Lowercase<string & k>>>>]: Input[k]};
type IsItWorking = Result<RandomKeys> extends CoercedKeys ? true: false;
// ^?
type test = Result<RandomKeys>
// ^?
type test2<T extends string> = Uncapitalize<Concat<SplitUnderscore<MaybeLowercase<T>>>>
type test3 = test2<"two-word-one"> // twoWordOne
type CrazyString<T extends string> = T extends `${infer Head}${infer Tail}`
? `${Capitalize<Head>}${Tail extends '' ? '' : '_'}${CrazyString<Tail>}` : T
type crazy = CrazyString<'vlad'>
// ^?
type CrazyString2<T extends string, Res extends string = ''> = T extends `${infer Head}${infer Tail}`
? CrazyString2<Tail, `${Res}${Res extends '' ? '' : '*'}${Head}`> : Res
type crazy2 = CrazyString2<'vladislav'>
type RequiredByKeys<T, K extends keyof T = keyof T> = {
[k in keyof T as k extends K ? k : never]-?: T[k]
} & {
[k in keyof T as k extends K ? never : k]: T[k]
} extends infer Result ? {
[k in keyof Result]: Result[k]
} : never
type t = RequiredByKeys<User, 'name' | 'age'>
// ^?
// {
// name: string;
// age: number;
// address?: string | undefined;
// }
const r: t = {
name: '2323',
age: 12123,
address: "223"
}
type PlusMinusParser<Input extends string> = Input extends `${infer Head}${infer _}` ? Head extends '+'|'-' ? Head : '' : ''
type PercentParser<Input extends string> = Input extends `${infer _}%` ? '%' : ''
type NumberParser<Input extends string> = Input extends `${PlusMinusParser<Input>}${infer Num}${PercentParser<Input>}` ? Num : ''
type PercentageParser<Input extends string> = [PlusMinusParser<Input>, NumberParser<Input>, PercentParser<Input>]
type Test = PercentageParser<'+555%'> // ["+", "555", "%"]
// ^?
type Flatten<Input extends unknown[]> =
Input extends [infer Head, ...infer Rest] ?
Head extends any[] ?
[...Flatten<Head>, ...Flatten<Rest>] : [Head, ...Flatten<Rest>] : Input
type Test = Flatten<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, 5]
// ^?
type Without<
Input extends number[],
NumberToDrop extends number | number[],
> = Input extends [infer Head, ...infer Rest extends number[]]
? // is number or number[]
NumberToDrop extends number | number[]
? // curr el === number
Head extends NumberToDrop
? // drop
[...Without<Rest, NumberToDrop>]
: NumberToDrop extends number[]
? Head extends NumberToDrop[number]
? // drop
[...Without<Rest, NumberToDrop>]
: [Head, ...Without<Rest, NumberToDrop>]
: [Head, ...Without<Rest, NumberToDrop>]
: // if NumberToDrop is not number or number[], return Input
Input
: Input;
type t = Without<[1, 2], 1>; // [2]
// ^?
type k = Without<[1, 2], [1]>; // 2
// ^?
type Count<
Input extends number,
Res extends 0[] = [],
> = Res["length"] extends Input
? Res
: Count<Input, [...Res, 0]>;
type r = Count<5>
// ^?
type GreaterThan<
Input1 extends number,
Input2 extends number,
Count1 extends 0[] = Count<Input1>,
Count2 extends 0[] = Count<Input2>,
> = Compare<Count1, Count2> extends true ? true : false;
type Compare<
A extends unknown[],
B extends unknown[],
> = A extends [infer Head, ...infer Rest]
? B extends [infer Head2, ...infer Rest2]
? Head extends Head2
? Compare<Rest, Rest2>
: false
: true
: false;
type t = Compare<[0,0,0], [0,0]> // true
// ^?
type k = GreaterThan<0,1> // false
// ^?
const createComponent = <Component extends Record<string,string>>(component: Component) => {
return <K extends keyof Component>(variant: K) => {
return component[variant]
}
}
const getButtonClasses = createComponent({
primary: "bg-blue-300",
secondary: "bg-green-300",
} as const);
const classes = getButtonClasses('secondary'); // 'bg-green-300'
// ^?
type FilterPrimitivesAndArrays<T extends unknown[]> = T extends [
infer Head,
...infer Rest,
]
? Head extends Record<any, any>
? Head extends [] | unknown[]
? [...FilterPrimitivesAndArrays<Rest>]
: [Head, ...FilterPrimitivesAndArrays<Rest>]
: [...FilterPrimitivesAndArrays<Rest>]
: T;
// return only objects
type t2 = FilterPrimitivesAndArrays<[{ kek: 1 }, 2, {}, [], null, undefined]>; // [{kek: 1}, {}]
// ^?
type EmptyArray<T extends unknown[]> = T['length'] extends 0 ? true : false;
type FalsyValues = 0 | '' | false | null | undefined
type EmptyObject<T extends {}> = keyof T extends never ? true : false
type Includes<Arr extends unknown[], Item extends unknown> = Arr extends [
infer Head,
...infer Rest
]
? Equal<Head, Item> extends true
? true
: Includes<Rest, Item>
: false;
type incl = Includes<[1, 2], 2>;
// ^?
type Unique<
Arr extends unknown[],
Result extends unknown[] = []
> = Arr extends [infer Head, ...infer Rest]
? Includes<Result, Head> extends true
? Unique<Rest, [...Result]>
: Unique<Rest, [...Result, Head]>
: Result;
type t = Unique<[1, 1, 2, 2, 3, 3]>; // [1,2,3]
// ^?
type MergeObjects<Object extends Record<string, string>> = {
[Prop in keyof Object]: Object[Prop];
};
type AppendToObject<
Object extends {},
Key extends keyof any,
Value extends unknown
> = MergeObjects<Object & { [key in Key]: Value }>;
type AppendToObject2<T, K extends keyof any, V> = {
[key in keyof T | K]: key extends keyof T ? T[key] : V;
};
type t = AppendToObject<{ kek: "23" }, "val", 5>; // {kek: '23'; val: 5}
// ^?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment