Vinz243
Vinz243

Reputation: 9958

Type 'string' is not assignable to type '"foo"'

I have this code:

const baseUpstreamImpl = (
  client: IClient,
): ProtocolExtension<
  {
    foobar: (buff: Buffer) => Promise<string>;
  }
> => ({
  name: 'base upstream impl',
  type: 'upstream',
  messageCreators: {
    foobar: (buff: Buffer)  =>
      Promise.resolve({
        type: 'foobar',
        payload: buff,
      }),
  },
});

This . is the error I get for the foobar function:

[ts]
Type '(buff: Buffer) => Promise<{ type: string; payload: Buffer; }>' is not assignable to type '(...args: any[]) => Promise<IMessage<{ foobar: (buff: Buffer) => Promise<string>; }, "foobar">>'.
  Type 'Promise<{ type: string; payload: Buffer; }>' is not assignable to type 'Promise<IMessage<{ foobar: (buff: Buffer) => Promise<string>; }, "foobar">>'.
    Type '{ type: string; payload: Buffer; }' is not assignable to type 'IMessage<{ foobar: (buff: Buffer) => Promise<string>; }, "foobar">'.
      Types of property 'type' are incompatible.
        Type 'string' is not assignable to type '"foobar"'.
baseUpstreamImpl.ts(11, 5): The expected type comes from property 'foobar' which is declared here on type '{ foobar: (...args: any[]) => Promise<IMessage<{ foobar: (buff: Buffer) => Promise<string>; }, "foobar">>; }'
(property) foobar: (...args: any[]) => Promise<IMessage<{
    foobar: (buff: Buffer) => Promise<string>;
}, "foobar">>

And here are my types:

type EnforcePromise<T, P = any> = T extends Promise<P> ? T : Promise<T>;

type Promised<T> = {
  [P in keyof T]: (...args: InferArgs<T[P]>) => EnforcePromise<InferType<T[P]>>
};

type Filter<T, Cond, U extends keyof T = keyof T> = {
  [K in U]: T[K] extends Cond ? K : never
}[U];

type EventKey<T> = Filter<T, (...args: any[]) => any>;


interface IMessage<T, K extends EventKey<T> = EventKey<T>> {
  type: K;
  payload: InferArgs<T[K]>;
}


export interface ProtocolExtension<T, U, R = {}> {
  name: string;
  type: 'upstream' | 'downstream';
  handlers: Promised<T>;
  messageCreators: {
    [K in keyof U]: (
      ...args: any[]
    ) => Promise<IMessage<U>>
  };
}

Upvotes: 0

Views: 456

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250256

There are two problems in the code both related to the way typescript infers types for literals.

The first one (and the error message you have) is the fact that type: 'foobar' will infer type to be string (in the absence of an imediate reason to infer it to the string literal type 'foobar'). The solution to this is to use a type assertion to the string literal type manually.

After we fix this issue we have a new error on the payload. payload should be InferArgs<T[K]> so it should be tuple with the parameter of the function. You only assign a single value to it. If we were to write [buf] we would have a similar problem to the string literal type issue above. Namely array literal would be typed as an array not a tuple (again in the absence of an imediate reason to type it as a tuple type).

There is also a third issue, the absence of the handelrs property, but taht is probably just an oversight.

const baseUpstreamImpl = (
    client: {},
): ProtocolExtension<
{},
{
    foobar: (buff: {}) => Promise<string>;
}
> => ({
    name: 'base upstream impl',
    type: 'upstream',
    handlers: null as any,
    messageCreators: {
        foobar: (buff: {}) =>
            Promise.resolve({
                type: 'foobar' as 'foobar',
                payload: [buff] as [{}],
            }),
    },
});

Upvotes: 1

Related Questions