GithubHelp home page GithubHelp logo

nartc / ngxtension-platform Goto Github PK

View Code? Open in Web Editor NEW
465.0 17.0 66.0 5.22 MB

Utilities for Angular

Home Page: https://ngxtension.netlify.app/

License: MIT License

TypeScript 99.83% JavaScript 0.02% HTML 0.06% CSS 0.09%

ngxtension-platform's Introduction

NG Extension Platform

NPM Version NPM

All Contributors

A collection of utilities for Angular.

Installation

The ng-add schematic/generator installs the ngxtension package as well as turning on skipLibCheck TypeScript configuration if it hasn't been turned on. This allows your project to skip checking types for external libraries like ngxtension where typings might not be compatible with your project's strictness.

ng add ngxtension

or

npm install ngxtension
yarn add ngxtension
pnpm install ngxtension
nx generate ngxtension:init

Utilities

Check the documentation.

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Chau Tran
Chau Tran

💻
Enea Jahollari
Enea Jahollari

💻
Phong Cao
Phong Cao

💻
Tiep Phan
Tiep Phan

💻
Daniele Morosinotto
Daniele Morosinotto

💻
Mateusz Stefańczyk
Mateusz Stefańczyk

💻
Tomer953
Tomer953

📖 💻
Laforge Thomas
Laforge Thomas

💻
Ion Prodan
Ion Prodan

💻
AmirHossein
AmirHossein

📖
Lucas Garcia
Lucas Garcia

💻
Nevzat Topçu
Nevzat Topçu

💻
Pascal Küsgen
Pascal Küsgen

📖
Dale Nguyen
Dale Nguyen

💻
Vinit Neogi
Vinit Neogi

⚠️
Josh Morony
Josh Morony

📖 💻
Glenn Latomme
Glenn Latomme

📖
Jeevan
Jeevan

📖
Diego Vilar
Diego Vilar

💻
Gianmarco Giummarra
Gianmarco Giummarra

📖
Evgeniy OZ
Evgeniy OZ

💻
Ajit Panigrahi
Ajit Panigrahi

📖
Alexander Castillo
Alexander Castillo

💻
Fiorelo Zere
Fiorelo Zere

💻
Robert Mulder
Robert Mulder

💻
Nelson Gutierrez
Nelson Gutierrez

📖
Justin Rassier
Justin Rassier

📖
Rafael Mestre
Rafael Mestre

📖
Swami
Swami

📖
Jim Drury (he/him)
Jim Drury (he/him)

💻
Robby Rabbitman
Robby Rabbitman

💻
Rainer Hahnekamp
Rainer Hahnekamp

💻
Matthieu Riegler
Matthieu Riegler

💻
isthatME
isthatME

📖
kkachniarz
kkachniarz

💻
Dominik
Dominik

📖
Add your contributions

This project follows the all-contributors specification. Contributions of any kind welcome!

ngxtension-platform's People

Contributors

nartc avatar eneajaho avatar allcontributors[bot] avatar joshuamorony avatar tomer953 avatar tomalaforge avatar dafnik avatar e-oz avatar robbyrabbitman avatar dmorosinotto avatar kkachniarz220 avatar fiorelozere avatar lcsga avatar pascalmh avatar vneogi199 avatar timdeschryver avatar rlmestre avatar jeevanmahesha avatar wanoo21 avatar palexcast avatar isthatme avatar tieppt avatar swami-sanapathi avatar sergidt avatar robbaman avatar rainerhahnekamp avatar develite98 avatar nevzatopcu avatar nelsongutidev avatar jeanmeche avatar

Stargazers

Chirag Chikhaliya avatar Brezhniev Ivan avatar Kai Schönberger avatar Ferdy Tarawan avatar Hakan avatar Michał Jastrzębowski avatar Cenlun Chung Po Lun avatar Kirill Larin avatar Shuaib hasan akib avatar Daranix avatar Max Smirnov avatar Enver avatar Klodian shaba avatar Ruben avatar Faradzhli Khalik avatar  avatar Marian Šimeček avatar Oto Dočkal avatar Marius B. avatar firej avatar Fabian Böller avatar Arjun PJ avatar Carles Albert avatar Alex avatar  avatar  avatar  avatar Paulo Henrique Rohling avatar Eyüp Erdoğan avatar Chopper avatar Florian avatar Rémi Bouguermouh avatar Timon avatar Loudghiri Ahmed avatar Neilx avatar Philipp John avatar Peter Šándor avatar Josh Allen avatar Oleksandr Dudynets avatar Nidal Bayan avatar Joe avatar Aleksei Sapozhnikov avatar  avatar Jess avatar Alejandro Castañeda avatar Lorenzo R avatar  avatar Touhid Rahman avatar Mohammed Hamza avatar  avatar chenxiang avatar Carles Albert Naveda Pallarès avatar Luke Jin avatar Maurizio Casciano avatar Mofid Juboqji avatar Jamie Burton avatar  avatar Pier Daniele Cretara avatar Mikal Callahan avatar Ehsan avatar Mateo Tibaquirá avatar Adrian Romanski avatar Matthieu Normand avatar Kevin K. avatar  avatar Piotr Ziółko avatar  avatar Mikhail Efanov avatar Stefano Marchisio avatar Thams avatar Tim Wiedemann avatar Mordechai Dror avatar René avatar ELTAYEB ALI aka Fadie avatar Ethan Tuning avatar Awab Abdoun avatar Matteo Luigi Tommasi avatar  avatar Felix Herold avatar Eirik Bjørndal avatar Estelle Picq avatar flynn.park avatar Logan Withers avatar Dmitry Yatsishin avatar  avatar Alexandra avatar Nilesh Namdeo Mali avatar  avatar Iker Larrea avatar Marco Bortolazzo avatar  avatar Timur Mishagin avatar J. Degand avatar  avatar  avatar Mateus Ribeiro Bossa avatar Dmytro Baltak avatar Marc J. Schmidt avatar Galambos Mihály avatar Magdalena Bober avatar

Watchers

Jits avatar Martin avatar Sysmat avatar Kelvin Li avatar Sergey Glova avatar Andreas Krummsdorf avatar Mariusz Dugin avatar Erik Fahlén avatar Hoàng Việt avatar  avatar Jeffrey Bosch avatar Ajit Panigrahi avatar  avatar  avatar  avatar Petrus Nguyễn Thái Học avatar Phuong Tran Dong avatar

ngxtension-platform's Issues

RFC function for mapping observable to loading, success, and error states

I have this operator function that I frequently use to handle simple async actions. For a lack of a better term, I called it switchToCompletion. The function aims to simplify handling loading, success, and error states and make it easier to render in the UI.

vm$ = trigger$.pipe(
  switchToCompletion((x) => api.doAction(x))
) 
<div *ngIf="vm$ | async as vm">
    <div *ngIf="vm.status === 'loading'">Loading...</div>
    <div *ngIf="vm.status === 'completed'">Completed: {{ vm.value }}</div>
    <div *ngIf="vm.status === 'failed'">Failed: {{ vm.message }}</div>
</div>

It converts an observable into another observable which emits the following type:

type Completion<Value> =
  | {
      status: "loading";
    }
  | {
      status: "completed";
      value: Value;
    }
  | {
      status: "failed";
      message: string;
      error: any;
    };

Roughly, the implementation I had in mind looks like this:

export const switchToCompletion = <Input, Output>(
  fn: (x: Input) => Observable<Output>
): OperatorFunction<Input, Completion<Output>> =>
  switchMap((value: Input) =>
    concat(
      of({ status: "loading" }),
      fn(value).pipe(
        map((value) => ({ status: "completed", value })),
        catchError((error) =>
          of({
            status: "failed",
            message: error.message,
            error,
          })
        )
      )
    )
  );

I'm unsure if there's a better and simpler approach to achieve this functionality or if I'm overthinking it. Any thoughts or suggestions would be awesome! Thanks!

rxjs Helper

-filterArray and mapArray to filter and map on array value => avoid doing the obs$.pipe(map(arr => arr.filter(...)))

  • filterNullUndefined to filter null and undefined values on observable steams

[feat] add the `subscribe`, `unsubscribe` and `finalize` notifs on the `debug` operator

As the debug operator takes a TapObserver as an argument and not simply a regular Observer, how about adding the 3 missing notifs to it?

Maybe we shouldn't add them by default, but add options defaulted to falsy for each 3 of them:

export function debug<T>(
    tag: string,
    extraNotifs?: { subscribe?: boolean; unsubscribe?: boolean; finalize?: boolean }
)  {
    const formatNotif = (notif: string, data?: unknown) => `${new Date().toISOString()} [${tag}: ${notif}]${data ? ` ${data}` : ""}`;
    return tap<T>({
        next: (value) => console.log(formatNotif("Next", value)),
        error: (err) => console.error(formatNotif("Error", err)),
        complete: () => console.warn(formatNotif("Completed")),
        subscribe: () => extraNotifs?.subscribe && console.log(formatNotif("Subscribed")),
        unsubscribe: () => extraNotifs?.unsubscribe && console.log(formatNotif(`Unsubscribed`)),
        finalize: () => extraNotifs?.finalize && console.log(formatNotif(`Finalized`)),
    });
}

What do you think?

[RFC] minimal signal store

Hi folks,

I'm presenting signalStore as a minimal Signal Store to ease the pain of managing state with Signal.

Target

I know that most folks would probably use some State Management libraries: ngrx, rx-angular, state-adapt etc... with Angular. However, signalStore() would make sense for folks who build Angular libraries where we do not want to depend on a full-fledged state management library.

API

type SignalStore<State extends object> = {
  select<
    Key1 extends keyof State,
    Key2 extends keyof State[Key1],
    Key3 extends keyof State[Key1][Key2],
    Key4 extends keyof State[Key1][Key2][Key3],
  >(
    key1: Key1,
    key2: Key2,
    key3: Key3,
    key4: Key4,
    options?: CreateComputedOptions<State[Key1][Key2][Key3][Key4]>,
  ): Signal<State[Key1][Key2][Key3][Key4]>;
  select<
    Key1 extends keyof State,
    Key2 extends keyof State[Key1],
    Key3 extends keyof State[Key1][Key2],
  >(
    key1: Key1,
    key2: Key2,
    key3: Key3,
    options?: CreateComputedOptions<State[Key1][Key2][Key3]>,
  ): Signal<State[Key1][Key2][Key3]>;
  select<Key1 extends keyof State, Key2 extends keyof State[Key1]>(
    key1: Key1,
    key2: Key2,
    options?: CreateComputedOptions<State[Key1][Key2]>,
  ): Signal<State[Key1][Key2]>;
  select<Key extends keyof State>(
    key: Key,
    options?: CreateComputedOptions<State[Key]>,
  ): Signal<State[Key]>;
  select(options?: CreateComputedOptions<State>): Signal<State>;

  get<
    Key1 extends keyof State,
    Key2 extends keyof State[Key1],
    Key3 extends keyof State[Key1][Key2],
    Key4 extends keyof State[Key1][Key2][Key3],
  >(
    key1: Key1,
    key2: Key2,
    key3: Key3,
    key4: Key4,
  ): State[Key1][Key2][Key3][Key4];
  get<
    Key1 extends keyof State,
    Key2 extends keyof State[Key1],
    Key3 extends keyof State[Key1][Key2],
  >(
    key1: Key1,
    key2: Key2,
    key3: Key3,
  ): State[Key1][Key2][Key3];
  get<Key1 extends keyof State, Key2 extends keyof State[Key1]>(
    key1: Key1,
    key2: Key2,
  ): State[Key1][Key2];
  get<Key extends keyof State>(key: Key): State[Key];
  get(): State;

  set(state: Partial<State> | ((previous: State) => Partial<State>)): void;
  patch(state: Partial<State>): void;

  state: Signal<State>;
};
  • select() accepts keys and nested keys (up to 4 levels). select() caches and returns the computed Signal for the keys.
  • get() behaves similarly to select() but it returns the value imperatively instead of Signal
  • set() behaves similarly to WriteableSignal#update but the update is wrapped with untracked() so it is safe to use inside of effect() and other places.
  • patch() is a little unique. It behaves like set(), except the previous state is applied AFTER the incoming state. This ensures incoming undefined data doesn't override the previous data.
  • state is similar to select() without arguments.
  • internal _snapshot: this is a non-configurable, non-enumerable getter on the store object. Useful for debugging current state at the time the getter is read.

signalStore() accepts an initialState either as a Partial<State> or (storeApi: Pick<SignalStore<State>, 'get' | 'set' | 'patch'>) => Partial<State>

  • the function version is particularly helpful when your initial state needs some computation / logic you want to encapsulate in the signalStore() function.

https://gist.github.com/nartc/b34a6000d2f2e2c552842d7281ca356e

untracked or not

signalStore current implementation wraps update calls in untracked. This is because, in Angular Three's Renderer, things invoke Signal updates during Render, which causes issues with "Update Signal during render". I personally would like to keep the behavior, but we can definitely make it configurable via signalStore() signature.

Usage

const store = signalStore({ 
  foo: 'default foo', 
  bar: { baz: 'nested', quz: 123 } 
});

// select lazily creates Signal and caches the created computed
const foo = store.select('foo'); // Signal<string>

// select nested property
const baz = store.select('bar', 'baz'); // Signal<string>

// select also accepts CreateComputedOptions
const bar = store.select('bar', { equal: Object.is }); // Signal<{ baz: string, quz: number }>

// read value imperatively at this point
const fooValue = store.get('foo'); // string;

// same nested property syntax
const bazValue = store.get('bar', 'baz');

// state is the readonly Signal
const state = store.state; // Signal<{ foo: string, bar: { baz: string, quz: number } }>

// want to control the computed Signal of the whole store, use select()
const customState = store.select({ equal: equalityFn });

// update state
store.set({ foo: 'updated foo' }); // accepts Partial state
store.set(prev => ({ bar: { ...prev.bar, quz: prev.bar.quz + 1 } }); // accepts update fn that can return Partial state

// (rare usage, Angular Three uses this API) patch state
store.patch({ foo: 'updated foo' }); // state.get('foo') === 'updated foo'
store.patch({ foo: undefined }); // state.get('foo') === 'updated foo'; using patch WILL NOT update the state if the value is "undefined". This API helps with cases where some Inputs have default values and those default values should NOT be overridden by "undefined" values

store.set({ foo: undefined }); // state.get('foo') === undefined; using set WILL ALWAYS update the state

// (rare usage, Angular Three uses this API) functional store creation
signalStore<Counter>(({ set, get, select }) => {
  /* some internal custom logic to the store creation phase */

  const count = select('count'); // Signal<number>

  return {
    count: 1,
    incrementCount: 0,
    decrementCount: 0,
    increment: () => set(prev => ({ count: prev.count + 1, incrementCount: prev.incrementCount + 1 })),
    decrement: () => set(prev => ({ count: prev.count - 1, decrementCount: prev.decrementCount + 1 })),
    double: computed(() => count() * 2)
  }
})

Check out one example from Angular Three

Swap computedFrom<Input, Output> to computedFrom<Output, Input>?

Hello @nartc and @eneajaho,

After a talk with Enea on Twitter, I've shown that maybe we could improve computedFrom type inference.

Right now, we can't easily tell computedFrom what kind of Output type we want it to be unless we specify the Input params first.

Right now we need to do this:

someNumber = signal<number | null>(null);

computedNumberToSomething = computedFrom<[number | null], number>(
    [this.someNumber],
    pipe(map(([someNumber]) => someNumber ?? 0))
);

Could we have something like:

someNumber = signal<number | null>(null);

computedNumberToSomething = computedFrom<number>(
    [this.someNumber],
    pipe(map(([someNumber]) => someNumber ?? 0))
);

It would be needed to swap <Input, Output> to <Output, Input> in computedFrom.

Second feedback:

Create an overloaded function that also accepts a non readonly array, so we can:

someNumber = signal<number | null>(null);

computedNumberToSomething = computedFrom<number>(
    this.someNumber,
    pipe(map(([someNumber]) => someNumber ?? 0))
);

What yall think?

Renato

[chore] Request for a little Nx guide to CONTRIBUTION.md

Hi can you please add a "little guide" to CONTRIBUTION.md to correctly setup a new "secondary-entrypoint" to the lib, for help the dev workflow for the "non-NX-addicted" devs like me 😜

I know, I must learn to use it (and maybe this is the right time for me) but some help is really appreciated
Thanks

[feat] inject is intersecting

import {
	DestroyRef,
	ElementRef,
	inject,
	Injectable,
	Injector,
	NgZone,
	runInInjectionContext,
} from '@angular/core';
import { Subject } from 'rxjs';
import { assertInjector } from 'ngxtension/assert-injector';
import { injectDestroy } from 'ngxtension/inject-destroy';

@Injectable({ providedIn: 'root' })
export class IsInViewportService {
	private ngZone = inject(NgZone);

	#observerListeners = new Map<Element, Subject<IntersectionObserverEntry>>();

	#observer?: IntersectionObserver;

	#createObserver() {
		this.#observer = this.ngZone.runOutsideAngular(() => {
			return new IntersectionObserver(entries => {
				for (const entry of entries) {
					this.#observerListeners.get(entry.target)?.next(entry);
				}
			});
		});
	}

	observe(element: Element) {
		if (!this.#observer) {
			this.#createObserver();
		}

		if (this.#observerListeners.has(element)) {
			return this.#observerListeners.get(element)!;
		}

		this.#observerListeners.set(element, new Subject<IntersectionObserverEntry>());
		this.#observer?.observe(element);

		return this.#observerListeners.get(element)!;
	}

	unobserve(element: Element) {
		this.#observer?.unobserve(element);

		this.#observerListeners.get(element)?.complete();
		this.#observerListeners.delete(element);

		if (this.#observerListeners.size === 0) {
			this.#disconnect();
		}
	}

	#disconnect() {
		this.#observer?.disconnect();
		this.#observer = undefined;
	}
}

export interface InjectIsIntersectingOptions {
	injector?: Injector;
	element?: Element;
}

/**
 * Injects an observable that emits whenever the element is intersecting the viewport.
 * The observable will complete when the element is destroyed.
 * @param options
 */
export const injectIsIntersecting = (options?: InjectIsIntersectingOptions) => {
	const injector = assertInjector(injectDestroy, options?.injector);

	return runInInjectionContext(injector, () => {
		const el = options?.element ?? inject(ElementRef).nativeElement;
		const inInViewportService = inject(IsInViewportService);
		const destroyRef = inject(DestroyRef);

		const sub = inInViewportService.observe(el);

		destroyRef.onDestroy(() => {
			inInViewportService.unobserve(el);
		});

		return sub;
	});
};

computedFrom type guard when using filter

Expected:
computedFrom operator should allow type narrowing
Small example using filterNil() to reproduce:

n = signal<number | undefined | null>(3);

  // filter with typeguard is working
  x$ = of(this.n()).pipe(
    filter((x): x is number => Boolean(x)),
    map((x) => (x as number) + 1)
  );
 
  // filterNil has typeguard already
  y$ = of(this.n()).pipe(
    filterNil(),
    map((x) => x + 1)
  );

  // but with computedFrom it failes to narrow
  z$ = computedFrom(
    [this.n],
    pipe(
      filterNil(),
      map(([x]) => x + 1) // x is possibly null or undefined
    )
  );

z$ should act like y$
I can’t find why this happens, @nartc might help here

Originally posted by @tomer953 in #73 (comment)

[RFC] imitate the angular packages structure

As the name of the project suggests it, it is intended to be an extension of angular.

Thus, since everything implemented is related to the existing angular subentries, and as the project evolves pretty quickly, wouldn't it be easier to find wher IS what if we imitate the angular architecture with thé core, common, etc. packages?

[RFC] directive to vizualize change detection

Would it make them to provide a directive to test change detection inside your application ? To test or debug CD issues.

import { Directive, DoCheck, ElementRef, NgZone } from '@angular/core';

@Directive({
  selector: '[cd-flash]',
  standalone: true,
})
export class CDFlashingDirective implements DoCheck {
  constructor(private elementRef: ElementRef, private zone: NgZone) {}

  ngDoCheck(): void {
    this.cdRan();
  }

  public cdRan(): void {
    this.zone.runOutsideAngular(() => {
      this.elementRef.nativeElement.classList.add('!bg-orange-500');
      setTimeout(() => {
        this.elementRef.nativeElement.classList.remove('!bg-orange-500');
      }, 1000);
    });
  }
}

You just need to add it on a element to vizualise when CD is made inside that element.

What do you think ?

development - npm i does not work without --force

git clone https://github.com/nartc/ngxtension-platform.git && cd ngxtension-platform
npm i
npm i
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: @ngxtension-platform/[email protected]
npm ERR! Found: @typescript-eslint/[email protected]
npm ERR! node_modules/@typescript-eslint/parser
npm ERR!   dev @typescript-eslint/parser@"^6.7.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @typescript-eslint/parser@"^5.60.1" from @nx/[email protected]
npm ERR! node_modules/@nx/eslint-plugin
npm ERR!   dev @nx/eslint-plugin@"16.8.1" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.

Add support function for router NavigationEnd event

Let's add an util function for catching whenever router navigation NavigationEnd event occur

e.g

export const routerNavigationEnd = () => {
  const router = inject(Router);

  return router.events.pipe(
    filter((event): event is NavigationEnd => event instanceof NavigationEnd)
  );
};

I am eager to add it if you would be interested with that one.

[RFC] createInjectionToken generic type and optional factory

I'd like to provide some other suggestions.

How about adding support for the Generic type and marking the factory as optional?

So basically this:
export const TOKEN = new InjectionToken<MyType>();

becomes:
export const [injectFn, provideFn] = createInjectionToken<MyType>();

There's a problem though, I can't make it multi without providing the factory 🤷🏻‍♂️

Also, considering the code above, would this work?
provideFn(() => { const foo = inject(FOO); if (foo) return 'something' return 'something else' })

Instead of a simple value, to provide a factory!

This is how I manage it now:
image

[feat] add a keyboard shortcut directive

I was thinking of adding a keyboard shortcut directive like the one in the stackblitz I made, as an example!

NB: In the stackblitz, I added boolean flags, for ctrl, alt, etc. keys, but the DX may be improved if it was an array of keys instead, what do you think?

RFC add initialValue to computedFrom fn

I follow all your works (article/twit) about computedForm and various iterations, and then tried to add something to it: the optional initialValue changing a bit of the internal implementation, to not discover the initial values from Obs, but simply relay to the Angular toObservable option using "requireSync" here you can find the code, hope this can be considered or discussed...

I propose this because I agree that you can easily solve the problem of initial "sync value" for observables using startWith, but I was concerned of cases where the user forgot this and have failing code, adding simple optional to set the initialValue of the resulting Output type maybe and easy task, and at the end if the user just used the startWith in the observable the initial combineLatest have all ready values to compute the first emit, and calling the toObservable with requiredSync will NOT error, but if you have some "later Observable" where you forgot to use startWith and you DON'T pass initialValue it will generate the runtime error, so the user know how to fix the problem (maybe)

add debouncedSearch operator

we probably can create a util takes an api call and debounce time and create a proper debounce - search pipe.
the most commong use-case is autocomplete from a string valueChanges.
this pipe also support cancelation when a new value emitted and ignore old results

/**
 * Debounced search utility function.
 * 
 * @example
 * ```ts
 * const searchFn = (query: string) => this.apiService.findItems(query)
 * search$.pipe(debouncedSearch(300, searchFn))
 * ```
 * 
 * @param debounce Debounce time in milliseconds
 * @param searchFn The actual search function to call
 * @param args Additional arguments for searchFn
 * @returns OperatorFunction to pipe into an observable
 */
debouncedSearch<T, A extends any[]>(
  debounce: number,
  searchFn: (query: string, ...args: A) => Observable<T>,
  ...args: A
): OperatorFunction<string, T> {
  return (input$) =>
    input$.pipe(
      // Debounce incoming input events
      debounceTime(debounce),

      // Switch to new inner observable on each input
      switchMap((query: string) => 
        // Call the provided search function
        searchFn(query, ...args).pipe(
          // Discard any results from previous searches if a new search is made
          takeUntil(input$.pipe(debounceTime(debounce), skip(1))),

          // If an error occurs, return an empty observable
          catchError(() => EMPTY)
        )
      )
    );
}

RFC - do you mind adding some helpers related to Observables and or TypeScript types?

I think that this repo/project is mostly related to Angular helpers, but do you mind if I add something related to Observable operators: https://gist.github.com/dmorosinotto/f98370a39d9041358915b8a493cf3654
or TypeScript generic types helpers?
https://gist.github.com/dmorosinotto/394ad458bd5352c3869981771baeaf1d
I have even others but just asking what you think before opening proposal issues that maybe are not in scope/desired...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.