Last active
September 15, 2022 11:05
-
-
Save VladSez/e0a9b430cbee5fb33ddc475188d1be1d to your computer and use it in GitHub Desktop.
Some TS snippets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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