Skip to content

Commit 5cc2c13

Browse files
add $withType<TFn>() to restrict isomorphicFn
1 parent f4ce824 commit 5cc2c13

File tree

5 files changed

+82
-2
lines changed

5 files changed

+82
-2
lines changed

packages/start-client-core/src/createIsomorphicFn.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,36 @@ export interface ClientOnlyFn<TArgs extends Array<any>, TClient>
2121
) => IsomorphicFn<TArgs, TServer, TClient>
2222
}
2323

24+
type AnyFn = (...args: Array<any>) => any
25+
export interface ServerOnlyFnWithType<TArgs extends Array<any>, TReturnType>
26+
extends IsomorphicFn<TArgs, TReturnType> {
27+
client: (
28+
clientImpl: (...args: TArgs) => TReturnType,
29+
) => IsomorphicFn<TArgs, TReturnType, TReturnType>
30+
}
31+
32+
export interface ClientOnlyFnWithType<TArgs extends Array<any>, TReturnType>
33+
extends IsomorphicFn<TArgs, TReturnType, TReturnType> {
34+
server: (
35+
serverImpl: (...args: TArgs) => TReturnType,
36+
) => IsomorphicFn<TArgs, TReturnType, TReturnType>
37+
}
38+
39+
export interface IsomorphicFnWithType<TArgs extends Array<any>, TReturnType>
40+
extends IsomorphicFn<TArgs, TReturnType> {
41+
server: (
42+
serverImpl: (...args: TArgs) => TReturnType,
43+
) => ServerOnlyFnWithType<TArgs, TReturnType>
44+
client: (
45+
clientImpl: (...args: TArgs) => TReturnType,
46+
) => ClientOnlyFnWithType<TArgs, TReturnType>
47+
}
48+
2449
export interface IsomorphicFnBase extends IsomorphicFn {
50+
$withType: <TFn extends AnyFn>() => IsomorphicFnWithType<
51+
Parameters<TFn>,
52+
ReturnType<TFn>
53+
>
2554
server: <TArgs extends Array<any>, TServer>(
2655
serverImpl: (...args: TArgs) => TServer,
2756
) => ServerOnlyFn<TArgs, TServer>
@@ -35,7 +64,9 @@ export interface IsomorphicFnBase extends IsomorphicFn {
3564
// therefore we must return a dummy function that allows calling `server` and `client` method chains.
3665
export function createIsomorphicFn(): IsomorphicFnBase {
3766
return {
67+
$withType: () => {},
3868
server: () => ({ client: () => () => {} }),
3969
client: () => ({ server: () => () => {} }),
4070
} as any
4171
}
72+

packages/start-client-core/src/tests/createIsomorphicFn.test-d.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,45 @@ test('createIsomorphicFn with arguments', () => {
7070
expectTypeOf(fn2).toBeCallableWith(1, 'a')
7171
expectTypeOf(fn2).returns.toEqualTypeOf<string | number>()
7272
})
73+
74+
test('createIsomorphicFn with type', () => {
75+
const fn1 = createIsomorphicFn()
76+
.$withType<(a: string) => string>()
77+
.server((a) => {
78+
expectTypeOf(a).toEqualTypeOf<string>()
79+
return 'data'
80+
})
81+
.client((a) => {
82+
expectTypeOf(a).toEqualTypeOf<string>()
83+
return 'data'
84+
})
85+
expectTypeOf(fn1).toBeCallableWith('foo')
86+
expectTypeOf(fn1).returns.toEqualTypeOf<string>()
87+
88+
const fn2 = createIsomorphicFn()
89+
.$withType<(a: string, b: number) => boolean>()
90+
.client((a, b) => {
91+
expectTypeOf(a).toEqualTypeOf<string>()
92+
expectTypeOf(b).toEqualTypeOf<number>()
93+
return true as const
94+
})
95+
.server((a, b) => {
96+
expectTypeOf(a).toEqualTypeOf<string>()
97+
expectTypeOf(b).toEqualTypeOf<number>()
98+
return false as const
99+
})
100+
expectTypeOf(fn2).toBeCallableWith('foo', 1)
101+
expectTypeOf(fn2).returns.toEqualTypeOf<boolean>()
102+
103+
const _invalidFnReturn = createIsomorphicFn()
104+
.$withType<(a: string) => string>()
105+
.server(() => '1')
106+
// @ts-expect-error - invalid return type
107+
.client(() => 2)
108+
109+
const _invalidFnArgs = createIsomorphicFn()
110+
.$withType<(a: string) => string>()
111+
.server((a: string) => 'data')
112+
// @ts-expect-error - invalid argument type
113+
.client((a: number) => 'data')
114+
})

packages/start-plugin-core/tests/createIsomorphicFn/snapshots/client/createIsomorphicFnDestructured.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ function abstractedClientFn() {
1313
}
1414
const clientOnlyFnAbstracted = abstractedClientFn;
1515
const serverThenClientFnAbstracted = abstractedClientFn;
16-
const clientThenServerFnAbstracted = abstractedClientFn;
16+
const clientThenServerFnAbstracted = abstractedClientFn;
17+
const withTypeRestriction = () => 'client';

packages/start-plugin-core/tests/createIsomorphicFn/snapshots/server/createIsomorphicFnDestructured.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ function abstractedClientFn() {
1313
}
1414
const clientOnlyFnAbstracted = () => {};
1515
const serverThenClientFnAbstracted = abstractedServerFn;
16-
const clientThenServerFnAbstracted = abstractedServerFn;
16+
const clientThenServerFnAbstracted = abstractedServerFn;
17+
const withTypeRestriction = () => 'server';

packages/start-plugin-core/tests/createIsomorphicFn/test-files/createIsomorphicFnDestructured.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,8 @@ const serverThenClientFnAbstracted = createIsomorphicFn()
3333
const clientThenServerFnAbstracted = createIsomorphicFn()
3434
.client(abstractedClientFn)
3535
.server(abstractedServerFn)
36+
37+
const withTypeRestriction = createIsomorphicFn()
38+
.$withType<() => string>()
39+
.server(() => 'server')
40+
.client(() => 'client')

0 commit comments

Comments
 (0)