Skip to content

Commit

Permalink
feat: Fully support all path-to-regex features in RestEndpoint.path t…
Browse files Browse the repository at this point in the history
…ypes (#3054)
  • Loading branch information
ntucker authored May 7, 2024
1 parent bfc5338 commit bac0ca7
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 34 deletions.
5 changes: 5 additions & 0 deletions .changeset/few-carpets-cheat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@data-client/rest": patch
---

Support + and \* in RestEndpoint.path
18 changes: 18 additions & 0 deletions .changeset/violet-elephants-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"@data-client/rest": patch
---

Add support for {} to RestEndpoint.path


```ts
const getThing = new RestEndpoint({
path: '/:attr1?{-:attr2}?{-:attr3}?',
});

getThing({ attr1: 'hi' });
getThing({ attr2: 'hi' });
getThing({ attr3: 'hi' });
getThing({ attr1: 'hi', attr3: 'ho' });
getThing({ attr2: 'hi', attr3: 'ho' });
```
3 changes: 1 addition & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ jobs:
at: ~/
- run:
command: |
cd packages/endpoint
yarn run typecheck
yarn workspaces foreach -A --include @data-client/endpoint --include @data-client/rest run typecheck
unit_tests:
parameters:
Expand Down
4 changes: 3 additions & 1 deletion packages/rest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@
"build": "run build:lib && run build:legacy:lib && run build:bundle",
"dev": "run build:lib -w",
"prepare": "run build:lib",
"prepack": "run prepare && run build:bundle && run build:legacy:lib"
"prepack": "run prepare && run build:bundle && run build:legacy:lib",
"tsc:ci": "yarn g:tsc --project tsconfig.test.json",
"typecheck": "run tsc:ci"
},
"author": "Nathaniel Tucker <[email protected]> (https://github.com/ntucker)",
"funding": "https://github.com/sponsors/ntucker",
Expand Down
2 changes: 1 addition & 1 deletion packages/rest/src/__tests__/RestEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Entity, schema } from '@data-client/endpoint';
import { useController } from '@data-client/react';
import { useSuspense } from '@data-client/react';
import { CacheProvider } from '@data-client/react';
import { Article, CoolerArticle, CoolerArticleResource } from '__tests__/new';
import { CoolerArticle, CoolerArticleResource } from '__tests__/new';
import nock from 'nock';

import { makeRenderDataClient } from '../../../test';
Expand Down
27 changes: 16 additions & 11 deletions packages/rest/src/pathTypes.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
type OnlyOptional<S extends string> = S extends `${infer K}?` ? K : never;
type OnlyOptional<S extends string> =
S extends `${infer K}}?` ? K
: S extends `${infer K}?` ? K
: never;
type OnlyRequired<S extends string> = S extends `${string}?` ? never : S;

/** Parameters for a given path */
export type PathArgs<S extends string> =
PathKeys<S> extends never ?
// unknown is identity for intersection ('&')
unknown
: KeysToArgs<PathKeys<S>>;

/** Computes the union of keys for a path string */
export type PathKeys<S extends string> =
string extends S ? string
: S extends `${infer A}\\:${infer B}` ? PathKeys<A> | PathKeys<B>
: S extends `${infer A}\\?${infer B}` ? PathKeys<A> | PathKeys<B>
: S extends `${infer A}\\${':' | '?' | '+' | '*' | '{' | '}'}${infer B}` ?
PathKeys<A> | PathKeys<B>
: PathSplits<S>;

type PathSplits<S extends string> =
S extends `${string}:${infer K}${'/' | ',' | '%' | '&'}${infer R}` ?
S extends (
`${string}:${infer K}${'/' | ',' | '%' | '&' | '+' | '*' | '{'}${infer R}`
) ?
PathSplits<`:${K}`> | PathSplits<R>
: S extends `${string}:${infer K}:${infer R}` ?
PathSplits<`:${K}`> | PathSplits<`:${R}`>
: S extends `${string}:${infer K}` ? K
: never;

/** Parameters for a given path */
export type PathArgs<S extends string> =
PathKeys<S> extends never ?
// unknown is identity for intersection ('&')
unknown
: KeysToArgs<PathKeys<S>>;

export type KeysToArgs<Key extends string> = {
[K in Key as OnlyOptional<K>]?: string | number;
} & (OnlyRequired<Key> extends never ? unknown
Expand Down
15 changes: 15 additions & 0 deletions packages/rest/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"extends": "./tsconfig",
"compilerOptions": {
"noEmit": true,
"emitDeclarationOnly": false,
"skipLibCheck": true,
"noImplicitAny": false,
"composite": false,
"rootDir": "../..",
"paths": {
"__tests__/*": ["__tests__/*"]
}
},
"include": ["typescript-tests"]
}
12 changes: 0 additions & 12 deletions packages/rest/tsconfig.typetest.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { Entity, schema } from '@data-client/endpoint';
import { useController, useSuspense } from '@data-client/react';
import { User } from '__tests__/new';

import createResource from '../createResource';
import RestEndpoint, { GetEndpoint, MutateEndpoint } from '../RestEndpoint';
import createResource from '../src/createResource';
import RestEndpoint, { GetEndpoint, MutateEndpoint } from '../src/RestEndpoint';

it('RestEndpoint construct and extend with typed options', () => {
new RestEndpoint({
Expand Down Expand Up @@ -574,3 +574,91 @@ it('should handle more open ended type definitions', () => {
explicit.push();
};
});

() => {
const getThing = new RestEndpoint({
path: '/:id*:bob',
});

getThing({ id: 5, bob: 'hi' });
// @ts-expect-error
getThing({ id: 'hi' });
// @ts-expect-error
getThing({ bob: 'hi' });
// @ts-expect-error
getThing(5);
};
() => {
const getThing = new RestEndpoint({
path: '/:id+:bob',
});

getThing({ id: 5, bob: 'hi' });
// @ts-expect-error
getThing({ 'id+': 5, bob: 'hi' });
// @ts-expect-error
getThing({ id: 'hi' });
// @ts-expect-error
getThing({ bob: 'hi' });
// @ts-expect-error
getThing(5);
};
() => {
const getThing = new RestEndpoint({
path: '/:id\\+:bob',
});

getThing({ id: 5, bob: 'hi' });
// @ts-expect-error
getThing({ 'id+': 5, bob: 'hi' });
// @ts-expect-error
getThing({ id: 'hi' });
// @ts-expect-error
getThing({ bob: 'hi' });
// @ts-expect-error
getThing(5);
};
() => {
const getThing = new RestEndpoint({
path: '/:id:bob+',
});

getThing({ id: 5, bob: 'hi' });
// @ts-expect-error
getThing({ id: 5, 'bob+': 'hi' });
// @ts-expect-error
getThing({ id: 'hi' });
// @ts-expect-error
getThing({ bob: 'hi' });
// @ts-expect-error
getThing(5);
};
() => {
const getThing = new RestEndpoint({
path: '/:foo/(.*)',
});

getThing({ foo: 'hi' });
// @ts-expect-error
getThing({});
// @ts-expect-error
getThing({ id: 'hi' });
// @ts-expect-error
getThing(5);
};
() => {
const getThing = new RestEndpoint({
path: '/:attr1?{-:attr2}?{-:attr3}?',
});

getThing({ attr1: 'hi' });
getThing({ attr2: 'hi' });
getThing({ attr3: 'hi' });
getThing({ attr1: 'hi', attr3: 'ho' });
getThing({ attr2: 'hi', attr3: 'ho' });
getThing({});
// @ts-expect-error
getThing({ random: 'hi' });
// @ts-expect-error
getThing(5);
};
Original file line number Diff line number Diff line change
Expand Up @@ -1054,7 +1054,7 @@ type ExtractCollection<S extends Schema | undefined> = S extends ({
type OnlyOptional<S extends string> = S extends `${infer K}?` ? K : never;
type OnlyRequired<S extends string> = S extends `${string}?` ? never : S;
/** Computes the union of keys for a path string */
type PathKeys<S extends string> = string extends S ? string : S extends `${infer A}\\:${infer B}` ? PathKeys<A> | PathKeys<B> : S extends `${infer A}\\?${infer B}` ? PathKeys<A> | PathKeys<B> : PathSplits<S>;
type PathKeys<S extends string> = string extends S ? string : S extends `${infer A}\\${':' | '?'}${infer B}` ? PathKeys<A> | PathKeys<B> : PathSplits<S>;
type PathSplits<S extends string> = S extends `${string}:${infer K}${'/' | ',' | '%' | '&'}${infer R}` ? PathSplits<`:${K}`> | PathSplits<R> : S extends `${string}:${infer K}:${infer R}` ? PathSplits<`:${K}`> | PathSplits<`:${R}`> : S extends `${string}:${infer K}` ? K : never;
/** Parameters for a given path */
type PathArgs<S extends string> = PathKeys<S> extends never ? unknown : KeysToArgs<PathKeys<S>>;
Expand Down
8 changes: 4 additions & 4 deletions website/src/components/Playground/editor-types/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1055,13 +1055,13 @@ type ExtractCollection<S extends Schema | undefined> = S extends ({
[K: string]: Schema;
} ? ExtractObject<S> : never;

type OnlyOptional<S extends string> = S extends `${infer K}?` ? K : never;
type OnlyOptional<S extends string> = S extends `${infer K}}?` ? K : S extends `${infer K}?` ? K : never;
type OnlyRequired<S extends string> = S extends `${string}?` ? never : S;
/** Computes the union of keys for a path string */
type PathKeys<S extends string> = string extends S ? string : S extends `${infer A}\\:${infer B}` ? PathKeys<A> | PathKeys<B> : S extends `${infer A}\\?${infer B}` ? PathKeys<A> | PathKeys<B> : PathSplits<S>;
type PathSplits<S extends string> = S extends `${string}:${infer K}${'/' | ',' | '%' | '&'}${infer R}` ? PathSplits<`:${K}`> | PathSplits<R> : S extends `${string}:${infer K}:${infer R}` ? PathSplits<`:${K}`> | PathSplits<`:${R}`> : S extends `${string}:${infer K}` ? K : never;
/** Parameters for a given path */
type PathArgs<S extends string> = PathKeys<S> extends never ? unknown : KeysToArgs<PathKeys<S>>;
/** Computes the union of keys for a path string */
type PathKeys<S extends string> = string extends S ? string : S extends `${infer A}\\${':' | '?' | '+' | '*' | '{' | '}'}${infer B}` ? PathKeys<A> | PathKeys<B> : PathSplits<S>;
type PathSplits<S extends string> = S extends (`${string}:${infer K}${'/' | ',' | '%' | '&' | '+' | '*' | '{'}${infer R}`) ? PathSplits<`:${K}`> | PathSplits<R> : S extends `${string}:${infer K}:${infer R}` ? PathSplits<`:${K}`> | PathSplits<`:${R}`> : S extends `${string}:${infer K}` ? K : never;
type KeysToArgs<Key extends string> = {
[K in Key as OnlyOptional<K>]?: string | number;
} & (OnlyRequired<Key> extends never ? unknown : {
Expand Down

0 comments on commit bac0ca7

Please sign in to comment.