Skip to content

Commit ab5244c

Browse files
authored
feat: Merge pull request #13 from seamapi/feat-filter-and-remap-helper
2 parents a9e9637 + 80eec65 commit ab5244c

File tree

6 files changed

+123
-29
lines changed

6 files changed

+123
-29
lines changed

src/helpers.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { Replace, Except } from "type-fest"
2+
import { APIDef, RouteDef } from "./route-def"
3+
4+
/**
5+
* Use this to select a subset of your routes and strip a prefix from the route path.
6+
*
7+
* Not currently supported for the RouteDef[] input syntax.
8+
*/
9+
export type FilterByAndStripPrefix<
10+
Prefix extends `/${string}/`,
11+
API extends APIDef
12+
> = API extends Record<string, RouteDef>
13+
? {
14+
[K in keyof API as Replace<
15+
K extends string ? K : never,
16+
Prefix,
17+
"/"
18+
>]: K extends `${Prefix}${string}`
19+
? Except<API[K], "route"> & {
20+
route: Replace<API[K]["route"], Prefix, "/">
21+
}
22+
: never
23+
}
24+
: never

src/http-method.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export type HTTPMethod =
2+
| "GET"
3+
| "POST"
4+
| "PUT"
5+
| "DELETE"
6+
| "PATCH"
7+
| "OPTIONS"
8+
| "HEAD"

src/index.ts

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,7 @@
11
import type { AxiosResponse, AxiosRequestConfig, AxiosInstance } from "axios"
22
import type { Except, SetRequired, Simplify, Split } from "type-fest"
3-
4-
export type HTTPMethod =
5-
| "GET"
6-
| "POST"
7-
| "PUT"
8-
| "DELETE"
9-
| "PATCH"
10-
| "OPTIONS"
11-
| "HEAD"
12-
13-
export type RouteDef = {
14-
route: string
15-
method: HTTPMethod
16-
queryParams?: Record<string, any>
17-
jsonBody?: Record<string, any>
18-
commonParams?: Record<string, any>
19-
formData?: Record<string, any>
20-
jsonResponse?: Record<string, any>
21-
// TODO support error responses
22-
}
23-
24-
export type APIDef = Record<string, RouteDef> | RouteDef[]
25-
26-
export type APIDefToUnion<Routes extends APIDef> = Routes extends RouteDef[]
27-
? Routes[number]
28-
: Routes extends Record<string, RouteDef>
29-
? Routes[keyof Routes]
30-
: never
3+
import { APIDef, APIDefToUnion, RouteDef } from "./route-def"
4+
import { HTTPMethod } from "./http-method"
315

326
// Given a union of {method: "GET" | "POST", ...} | {method: "PATCH", ...} converts it to a union of
337
// {method: "GET", ...} | {method: "POST", ...} | {method: "PATCH", ...}
@@ -211,3 +185,6 @@ export const createTypedURLSearchParams = <T>(
211185
): TypedURLSearchParams<T> => {
212186
return new URLSearchParams(data as any)
213187
}
188+
189+
export * from "./helpers"
190+
export * from "./route-def"

src/route-def.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { HTTPMethod } from "./http-method"
2+
3+
export type RouteDef = {
4+
route: string
5+
method: HTTPMethod
6+
queryParams?: Record<string, any>
7+
jsonBody?: Record<string, any>
8+
commonParams?: Record<string, any>
9+
formData?: Record<string, any>
10+
jsonResponse?: Record<string, any>
11+
// TODO support error responses
12+
}
13+
14+
export type APIDef = Record<string, RouteDef> | RouteDef[]
15+
16+
export type APIDefToUnion<Routes extends APIDef> = Routes extends RouteDef[]
17+
? Routes[number]
18+
: Routes extends Record<string, RouteDef>
19+
? Routes[keyof Routes]
20+
: never

tests/example-route-types.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,38 @@ export type ExampleRouteTypes4 = {
120120
}
121121
}
122122

123+
export type DifferentDomainsAsPathsExample = {
124+
"/example.com/things/create": {
125+
route: "/example.com/things/create"
126+
method: "POST"
127+
queryParams: {}
128+
commonParams: {}
129+
formData: {}
130+
jsonResponse: {
131+
thing: {
132+
thing_id: string
133+
name: string
134+
created_at: string
135+
}
136+
}
137+
}
138+
139+
"/foo.example.com/things/create": {
140+
route: "/foo.example.com/things/create"
141+
method: "POST"
142+
queryParams: {}
143+
commonParams: {}
144+
formData: {}
145+
jsonResponse: {
146+
thing_from_foo: {
147+
thing_id: string
148+
name: string
149+
created_at: string
150+
}
151+
}
152+
}
153+
}
154+
123155
export type WildcardAndSpecificEndpointExample = {
124156
"/things/[thing_id]": {
125157
route: "/things/[thing_id]"

tests/main.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import { toFormData } from "axios"
22
import test from "ava"
33
import { expectTypeOf } from "expect-type"
4-
import { TypedAxios, createTypedURLSearchParams } from "../src"
54
import {
5+
FilterByAndStripPrefix,
6+
TypedAxios,
7+
createTypedURLSearchParams,
8+
} from "../src"
9+
import {
10+
DifferentDomainsAsPathsExample,
611
ExampleRouteTypes1,
712
ExampleRouteTypes3,
813
ExampleRouteTypes4,
@@ -132,3 +137,31 @@ test("can create FormData", async (t) => {
132137
t.is(encodedData.get("resourceId"), formData.resourceId.toString())
133138
t.is(encodedData.get("slug"), formData.slug)
134139
})
140+
141+
test.failing("", async (t) => {
142+
const axios: TypedAxios<
143+
FilterByAndStripPrefix<"/example.com/", DifferentDomainsAsPathsExample>
144+
> = null as any
145+
146+
const createResponse = await axios.post("/things/create", {})
147+
expectTypeOf(createResponse.data).toMatchTypeOf<{
148+
thing: {
149+
thing_id: string
150+
name: string
151+
created_at: string
152+
}
153+
}>()
154+
155+
const fooAxios: TypedAxios<
156+
FilterByAndStripPrefix<"/foo.example.com/", DifferentDomainsAsPathsExample>
157+
> = null as any
158+
159+
const fooCreateResponse = await fooAxios.post("/things/create", {})
160+
expectTypeOf(fooCreateResponse.data).toMatchTypeOf<{
161+
thing_from_foo: {
162+
thing_id: string
163+
name: string
164+
created_at: string
165+
}
166+
}>()
167+
})

0 commit comments

Comments
 (0)