cloudflare / cloudflare/capnweb

document tsconfig `lib`/`target` requirements

Open
#112 9 comments 0 reactions 0 assignees View on GitHub
documentation
Dominant language
TypeScript
Stars
4k
Forks
143
Avg merge
4d 6h
Merged PRs (30d)
7

Description

## Description

When using `newWebSocketRpcSession` or `newHttpBatchRpcSession` with a TypeScript generic parameter, the `RpcStub` type preserves primitive return types (string, number, boolean) but fails to preserve object/interface return types, returning `any` instead.

## Minimal Reproducible Example

```typescript
import { newWebSocketRpcSession, type RpcStub } from 'capnweb';

// Define types
type ApiMessage = { message: string; timestamp: string };

// Define API interface
interface MyApi {
getPrimitive(): Promise; // Returns primitive
getObject(): Promise; // Returns object
hello(name: string): Promise; // Returns object
}

// Create client
const apiStub = newWebSocketRpcSession('wss://example.com/api');

// Primitives work correctly ✅
const primitive = await apiStub.getPrimitive();
// ^? Type is 'string' ✅

// Objects return 'any' ❌
const object = await apiStub.getObject();
// ^? Type is 'any' ❌

const helloResult = await apiStub.hello('world');
// ^? Type is 'any' ❌
```

## Expected Behavior

All method return types should be preserved, regardless of whether they're primitives or objects:

```typescript
const primitive = await apiStub.getPrimitive();
// ^? string ✅

const object = await apiStub.getObject();
// ^? Should be: ApiMessage

const helloResult = await apiStub.hello('world');
// ^? Should be: ApiMessage
```

## Actual Behavior

Only primitive return types are preserved. Object/interface return types become `any`:

```typescript
const apiStub: RpcStub = newWebSocketRpcSession('wss://example.com/api');
// ^? Correctly shows: RpcStub

const primitive = await apiStub.getPrimitive();
// ^? string ✅ Works!

const object = await apiStub.getObject();
// ^? any ❌ Should be ApiMessage

const helloResult = await apiStub.hello('world');
// ^? any ❌ Should be ApiMessage

// No type checking for objects
console.log(helloResult.anythingGoes); // No error
```

## Root Cause

The `RpcStub` type definition appears to handle primitives correctly but wraps object return types in complex proxy types (for promise pipelining support) that resolve to `any` instead of the original type.

## Workaround

Manually cast each result:

```typescript
const helloResult = await apiStub.hello('world') as ApiMessage;
// ^? Now correctly typed as: ApiMessage ✅
```

## Environment

- **capnweb version**: 0.2.0
- **TypeScript version**: 5.8.3
- **Node version**: 22.15.1

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.